#include ".\gamefilesystem.h"

GameFileSystem::GameFileSystem(void)
{
	// no fp allocated yet!
	this->numAllocatedFp=0;
	this->fp=NULL;
	for (int i=0; i < MAX_FILES_POINTERS; i++)
		this->fpInit[i]=false;
}

GameFileSystem::~GameFileSystem(void)
{
	// chiude e distrugge tutti i fp allocati
	for (int i=0; i < MAX_FILES_POINTERS; i++) {
		if (this->fpInit[i]) {
			// chiude file se aperto
			if (this->fp[i])
				fclose(this->fp[i]);
			this->fp=NULL;;
			this->fpInit[i]=false;
		}
	}
	// libera puntatori allocati
	free(this->fp);
}

// apre file nel primo slot disponibile, torna l'handle o NULL
// se non può aprirlo o non abbiamo altri puntatori
int GameFileSystem::OpenFile(const char *filename, const char *mode, bool open_from_ext_res)
{
	// cerca un puntatore libero tra quelli allocabili
	int i, index;
	for (i=0; i < MAX_FILES_POINTERS; i++) {
		if (!this->fpInit[i]) {
			// possiamo creare qui
			index=i;
			break;
		}
	}

	// Realloca dimensione array se necessario
	if (index + 1  > this->numAllocatedFp) {

		// indice out of bounds, non possiamo allocare
		if (index+1 >= MAX_FILES_POINTERS)
			return NULL;

		this->numAllocatedFp = index + 1;
		this->fp=(FILE **)realloc(this->fp, sizeof(FILE *)*this->numAllocatedFp);
		this->fp[index]=NULL;
	}

	// se il file descriptor è giò aperto (??) chiudilo
	if (this->fp[index]) {
		PFrogConsole::instance()->addconsole(0, "CLOSING AN ALREADY OPENED FILE DESC: %d", index);
		fclose(this->fp[index]);
		this->fp[index]=NULL;
	}

	// path dove creare il file
	TCHAR lpFileName[512],lpFileName2[512],lpDirOnly[512];

	// extract exe path
	GetModuleFileName(NULL, lpFileName, 255);

	// get the last backslash
	for (i=_tcslen(lpFileName)-1; i>=0; i--) {
		if ( lpFileName[i] == _T('\\') ) {
			_stprintf(lpFileName2, _T("%.*s"), i+1, lpFileName); 
			break;
		}
	}
	
	if ( ! open_from_ext_res ) {
		
		// --- APRE DAL FILESYSTEM VIRTUAL INTERNO

		// crea il nostro nome di file (con .fbaff alla fine)
		// patch - 07 luglio 2008 - rimossa directory user_data
#if 0
		_stprintf(lpDirOnly, _T("%s%s"), lpFileName2, _T("userdata\\")); 
		// crea dir
		CreateDirectory(lpDirOnly, NULL);
#else
		_stprintf(lpDirOnly, _T("%s"), lpFileName2); 
#endif
		#ifdef ARM
			TCHAR finalfname[1024];
			MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, -1, finalfname, 1024);
			_stprintf(lpFileName, _T("%s%s.fbaff"), lpDirOnly, finalfname); 
		#else
			_stprintf(lpFileName, _T("%s%s.fbaff"), lpDirOnly, filename); 
		#endif
	
	} else {

		// --- APRE DALLE RISORSE ESTERNE (in questo caso il file deve esistere, quindi controllo prima in read mode!)
		#ifdef ARM
			TCHAR finalfname[1024];
			MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, -1, finalfname, 1024);
			_stprintf(lpFileName, _T("%s%s"), lpFileName2, finalfname); 
		#else
			_stprintf(lpFileName, _T("%s%s"), lpFileName2, filename); 
		#endif

		FILE *tmpfp = _tfopen(lpFileName, _T("rb"));
		if (!tmpfp) {
			PFrogConsole::instance()->addconsole(0, "CANNOT OPEN EXTERNAL RES FILE (IT MUST EXISTS)");
			return -1;
		}
	}

	// prova ad aprire il file
	if (strcmp(mode, "write") == 0 || strcmp(mode, "w") == 0) {
		this->fp[index] = _tfopen(lpFileName, _T("ab"));
	}
	else {
		if ( ! open_from_ext_res ) {
			if (!this->Exists(filename)) {
				FILE *tmpf = _tfopen(lpFileName, _T("w"));
				fclose(tmpf);
			}
		} 
		this->fp[index] = _tfopen(lpFileName, _T("rb"));
	}

	// segna che è stato inizializzato
	if (this->fp[index]) {
		PFrogConsole::instance()->addconsole(0, "OPENED FILE DESCRIPTOR: %d", index);
		this->fpInit[index]=true;
	} else {
		PFrogConsole::instance()->addconsole(0, "UNABLE TO OPEN FILE DESCRIPTOR: %d", index);
	}

	// torna l'index o -1 in caso di errore
	return (this->fp[index]?index:-1);
}

bool GameFileSystem::Exists(const char *filename) { return _mFileExists(filename, false); }
bool GameFileSystem::Delete(const char *filename) { return _mFileExists(filename, true); }

bool GameFileSystem::_mFileExists(const char *filename, bool deleteme)
{
	// path dove creare il file
	TCHAR lpFileName[512],lpFileName2[512];

	// extract exe path
	GetModuleFileName(NULL, lpFileName, 255);

	// get the last backslash
	for (int i=_tcslen(lpFileName)-1; i>=0; i--) {
		if ( lpFileName[i] == _T('\\') ) {
			_stprintf(lpFileName2, _T("%.*s"), i+1, lpFileName); 
			break;
		}
	}
	
	// crea il nostro nome di file (con .fbaff alla fine)
	// patch - 07 luglio 2008 - rimossa directory user_data
#if 0
	#ifdef ARM
		TCHAR finalfname[1024];
		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, -1, finalfname, 1024);
		_stprintf(lpFileName, _T("%s%s%s.fbaff"), lpFileName2, _T("userdata\\"), finalfname); 
	#else
		_stprintf(lpFileName, _T("%s%s%s.fbaff"), lpFileName2, _T("userdata\\"), filename); 
	#endif
#else
	#ifdef ARM
		TCHAR finalfname[1024];
		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, -1, finalfname, 1024);
		_stprintf(lpFileName, _T("%s%s.fbaff"), lpFileName2, finalfname); 
	#else
		_stprintf(lpFileName, _T("%s%s.fbaff"), lpFileName2, filename); 
	#endif
#endif
	// controlliamo se riusciamo ad aprire il file o cancelliamo se richiesto
	bool found=false;

	if (deleteme) {
		found=( DeleteFile(lpFileName) == TRUE ? true : false);
	} else {
		FILE *tmp=_tfopen(lpFileName, _T("r"));
		found=(tmp!=NULL);
		if (tmp)
			fclose(tmp);
	}

	return found;

}

bool GameFileSystem::CloseFile(int index)
{
	if (index >= MAX_FILES_POINTERS || !this->fpInit[index] )
		return false;

	if (this->fp[index]) {
		fclose(this->fp[index]);
		this->fpInit[index]=false;
		return true;
	} else {
		PFrogConsole::instance()->addconsole(0, "FDESC FLAG CLOSE SET, BUT IT WASN'T OPENED: %d", index);
		this->fpInit[index]=false;
		return true;
	}
}

int GameFileSystem::ReadBytes(int index, char *dest, int numbytes)
{
	if (index >= MAX_FILES_POINTERS || !this->fpInit[index] )
		return -1;

	// warning: mi aspetto che dest sia già allocato correttamente !
	return fread(dest, 1, numbytes, this->fp[index]);
}

int GameFileSystem::WriteBytes(int index, const char *source, int numbytes)
{
	if (index >= MAX_FILES_POINTERS || !this->fpInit[index] )
		return -1;

	return fwrite(source, 1, numbytes, this->fp[index]);
}

// come le funzioni C
int GameFileSystem::FSeek(int index, int whence, long offset)
{
	if (index >= MAX_FILES_POINTERS || !this->fpInit[index] )
		return -2;

	return fseek(this->fp[index], offset, whence);
} // void GameFileSystem::FSeek(int index, int whence, long offset)


// come le funzioni C
long GameFileSystem::FTell(int index)
{
	if (index >= MAX_FILES_POINTERS || !this->fpInit[index] )
		return -1;

	return ftell(this->fp[index]);
} // long GameFileSystem::FTell(int index)
