//
// Asura_Wii_NAND.h
//
// Created by KOR - 14 June 2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_WII_NAND_H__
#define __ASURA_WII_NAND_H__

#include "Core/Asura_PlatformBasics.h" // for Asura_UnicodeChar
#include <revolution/nand.h>
#include <revolution/tpl.h>

//#define ASURA_WII_NAND_USE_SAFE_CALLS (1)

// Constants/enums
#ifdef ASURA_WII_NAND_USE_SAFE_CALLS
const u_int uASURA_WII_NAND_SAFEOPEN_BUFFER_SIZE = 16 * 1024;
#endif
const u_int uASURA_WII_NAND_IO_BUFFER_SIZE = 16 * 1024;
const char szBANNER_FILENAME[] = "banner.bin";
// According to "NAND Function Introduction", there are a maximum of 32 files allowed in the home directory
const u_int uASURA_WII_NAND_MAX_FILES_PER_DIR = 32;

enum ASURA_WII_NAND_STATE
{
	ASURA_WII_NAND_IDLE,
	ASURA_WII_NAND_READ,
	ASURA_WII_NAND_WRITE,
	ASURA_WII_NAND_ERROR,
};

struct NANDCommandBlock;

struct Asura_Wii_NAND_WriteFileData
{
	NANDFileInfo	m_xFileInfo;
	char			m_szFilename[NAND_MAX_NAME + 1];
	const char*		m_pcData;
	u_int			m_uDataSize;
	bool			m_bOverwrite;
};

struct Asura_Wii_NAND_ReadFileData
{
	NANDFileInfo	m_xFileInfo;
	char			m_szFilename[NAND_MAX_NAME + 1];
	char*			m_pcBuffer;
	u_int			m_uBufferSize;
	u32				m_uBytesRemaining;
};

typedef void NANDCorruptCallback(void);

class Asura_Wii_NAND
{
public:
	static void Initialise();
	static void Shutdown();
	// For both reads and writes, the filename is copied, so it may be on the stack.
	// The maximum filename length is NAND_MAX_NAME ( = 12, i.e. 8.3 at the time of writing), not including null terminator.
	// Reads the entire file into the given buffer. If the file is larger than the buffer, simply fills the buffer.
	static bool ReadFile(const char* szFilename, void* pvBuffer, u_int uBufferSize, bool bAsync = false, u_int uOffset = 0);
	// Writes the buffer into a single new file. If the file already exists, it will delete it first if bOverwrite is true.
	static bool WriteFile(const char* szFilename, const void* pvData, u_int uSize, bool bOverwrite = true, bool bAsync = false, u_int uOffset = 0);
	// Deletes the file synchronously.
	static bool DeleteFile(const char* szFilename);

	// Returns the number of files in the home directory and fills out the list.
	static u_int ReadHomeDir();
	static const char* GetFilenameInHomeDir(u_int uFile);

	// Note: should be done after all other savedata files are written,
	// so that partially finished saves are not seen in the Wii menu.
	static bool WriteBanner(const Asura_UnicodeChar* wszTitle, const Asura_UnicodeChar* wszDesc, const char* szBannerTexture, const char* szIconTexture, bool bAsync = false);
	static bool DoesBannerExist();
	static bool DeleteBanner();

	// For checking available space
	static u_int BytesToNandBlocks(u_int uBytes) { return ((uBytes + NAND_FSBLOCK_SIZE - 1) / NAND_FSBLOCK_SIZE); }
	// In both of these, nodes + blocks for directory and banner are automatically added if necessary.
	// Use this for a single file
	static int CheckAvailableSpaceBytes(u_int uRequiredBytes);
	// Use this for multiple files. You must calculate the number of blocks for each file and sum them,
	// rather than summing the bytes then converting to blocks. 
	static int CheckAvailableSpaceBlocks(u_int uRequiredBlocks, u_int uNumFiles);

	// Returns size in bytes of the file specified, or -1 if it doesn't exist
	static int GetFileSize(const char* szFilename);
	static bool DoesFileExist(const char* szFilename) { return GetFileSize(szFilename) >= 0; }

	static void SetState(ASURA_WII_NAND_STATE eState) { s_eState = eState; }
	static ASURA_WII_NAND_STATE GetState() { return s_eState; }
	static bool IsBusy() { return IsWriting() || IsReading(); }
	static bool IsWriting() { return (s_eState == ASURA_WII_NAND_WRITE); }
	static bool IsReading() { return (s_eState == ASURA_WII_NAND_READ); }

	static void HandleReadError(s32 iResult, NANDCommandBlock* pxCloseCommandBlock = NULL);

	static void SetNANDCorruptCallback(NANDCorruptCallback* pfnCallback) { s_pfnNANDCorruptCallback = pfnCallback; }
#ifdef _DEBUG
	static void RunTests();
#endif

private:
	static void ChangeToHomeDir();

	static void WriteCallback_Open(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void WriteCallback_Write(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void WriteCallback_Close(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void WriteCallback_Finish(s32 iResult, NANDCommandBlock* pxCommandBlock);

	static void ReadCallback_Open(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_GetLength(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_Read(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_ReadBuffered(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_Close(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_CloseBuffered(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_CloseError(s32 iResult, NANDCommandBlock* pxCommandBlock);
	static void ReadCallback_Finish(s32 iResult, NANDCommandBlock* pxCommandBlock);

	static bool LoadTexture(const char* szFilename, void* pvBuffer, u_int uSize);

	static bool CreateBanner(const Asura_UnicodeChar* wszTitle, const Asura_UnicodeChar* wszDesc, const char* szBannerTexture, const char* szIconTexture);

	static volatile ASURA_WII_NAND_STATE s_eState;
	static Asura_Wii_NAND_WriteFileData s_xWriteFileData;
	static Asura_Wii_NAND_ReadFileData s_xReadFileData;
	static NANDCorruptCallback* s_pfnNANDCorruptCallback;
	// Buffers
#ifdef ASURA_WII_NAND_USE_SAFE_CALLS 
	static char s_acNandSafeOpenCopyBuffer[uASURA_WII_NAND_SAFEOPEN_BUFFER_SIZE];
#endif
	static char s_acNandIOBuffer[uASURA_WII_NAND_IO_BUFFER_SIZE];
	static char s_acDirectoryReadBuffer[OSRoundUp32B(uASURA_WII_NAND_MAX_FILES_PER_DIR * (NAND_MAX_NAME + 1))];
	static const char* s_aszFilenames[uASURA_WII_NAND_MAX_FILES_PER_DIR];
	static u32 s_uNumFilenames;
};

#endif//__ASURA_WII_NAND_H__