/******************************************************************************

 @File         PVRTResourceFile.cpp

 @Title        PVRTResourceFile.cpp

 @Version      

 @Copyright    Copyright (c) Imagination Technologies Limited.

 @Platform     ANSI compatible

 @Description  Simple resource file wrapper

******************************************************************************/

#include "PVRTResourceFile.h"
#include <stdio.h>
#include <string.h>

#include "PVRTResourceFile.h"
#include "PVRTString.h"
#include "PVRTMemoryFileSystem.h"

CPVRTString CPVRTResourceFile::s_ReadPath;

static void* LoadFileFunc(const char* pFilename, char** pData, size_t &size)
{
	size = 0;
	
	FILE* pFile = fopen(pFilename, "rb");

	if (pFile)
	{
		// Get the file size
		fseek(pFile, 0, SEEK_END);
		size = ftell(pFile);
		fseek(pFile, 0, SEEK_SET);

		// read the data
		char* pTmp = new char[size];
		size_t BytesRead = fread(pTmp, 1, size, pFile);

		if (BytesRead != size)
		{
			delete [] pTmp;
			pTmp = NULL;
			size = 0;
		}
		else
			*pData = pTmp;

		fclose(pFile);
		return pTmp;
	}

	return 0;
}

static bool ReleaseFileFunc(void* handle)
{
	if(handle)
	{
		delete[] (char*) handle;
		return true;
	}

	return false;
}

PFNLoadFileFunc CPVRTResourceFile::s_pLoadFileFunc = &LoadFileFunc;
PFNReleaseFileFunc CPVRTResourceFile::s_pReleaseFileFunc = &ReleaseFileFunc;

/*!***************************************************************************
@Function			SetReadPath
@Input				pszReadPath The path where you would like to read from
@Description		Sets the read path
*****************************************************************************/
void CPVRTResourceFile::SetReadPath(const char* const pszReadPath)
{
	s_ReadPath = (pszReadPath) ? pszReadPath : "";
}

/*!***************************************************************************
@Function			GetReadPath
@Returns			The currently set read path
@Description		Returns the currently set read path
*****************************************************************************/
CPVRTString CPVRTResourceFile::GetReadPath()
{
	return CPVRTString(s_ReadPath);
}

/*!***************************************************************************
@Function			SetLoadReleaseFunctions
@Input				pLoadFileFunc Function to use for opening a file
@Input				pReleaseFileFunc Function to release any data allocated by the load function
@Description		This function is used to override the CPVRTResource file loading functions. If
                    you pass NULL in as the load function CPVRTResource will use the default functions.
*****************************************************************************/
void CPVRTResourceFile::SetLoadReleaseFunctions(void* pLoadFileFunc, void* pReleaseFileFunc)
{
	if(pLoadFileFunc)
	{
		s_pLoadFileFunc = (PFNLoadFileFunc) pLoadFileFunc;
		s_pReleaseFileFunc = (PFNReleaseFileFunc) pReleaseFileFunc;
	}
	else
	{
		s_pLoadFileFunc = &LoadFileFunc;
		s_pReleaseFileFunc = &ReleaseFileFunc;
	}
}

/*!***************************************************************************
@Function			CPVRTResourceFile
@Input				pszFilename Name of the file you would like to open
@Description		Constructor
*****************************************************************************/
CPVRTResourceFile::CPVRTResourceFile(const char* const pszFilename) :
	m_bOpen(false),
	m_bMemoryFile(false),
	m_Size(0),
	m_pData(0),
	m_Handle(0)
{
	CPVRTString Path(s_ReadPath);
	Path += pszFilename;

	m_Handle = s_pLoadFileFunc(Path.c_str(), (char**) &m_pData, m_Size);
	m_bOpen = (m_pData && m_Size) != 0;

	if (!m_bOpen)
	{
		m_bOpen = m_bMemoryFile = CPVRTMemoryFileSystem::GetFile(pszFilename, (const void**)(&m_pData), &m_Size);
	}
}

/*!***************************************************************************
@Function			CPVRTResourceFile
@Input				pData A pointer to the data you would like to use
@Input				i32Size The size of the data
@Description		Constructor
*****************************************************************************/
CPVRTResourceFile::CPVRTResourceFile(const char* pData, size_t i32Size) :
	m_bOpen(true),
	m_bMemoryFile(true),
	m_Size(i32Size),
	m_pData(pData),
	m_Handle(0)
{
}

/*!***************************************************************************
@Function			~CPVRTResourceFile
@Description		Destructor
*****************************************************************************/
CPVRTResourceFile::~CPVRTResourceFile()
{
	Close();
}

/*!***************************************************************************
@Function			IsOpen
@Returns			true if the file is open
@Description		Is the file open
*****************************************************************************/
bool CPVRTResourceFile::IsOpen() const
{
	return m_bOpen;
}

/*!***************************************************************************
@Function			IsMemoryFile
@Returns			true if the file was opened from memory
@Description		Was the file opened from memory
*****************************************************************************/
bool CPVRTResourceFile::IsMemoryFile() const
{
	return m_bMemoryFile;
}

/*!***************************************************************************
@Function			Size
@Returns			The size of the opened file
@Description		Returns the size of the opened file
*****************************************************************************/
size_t CPVRTResourceFile::Size() const
{
	return m_Size;
}

/*!***************************************************************************
@Function			DataPtr
@Returns			A pointer to the file data
@Description		Returns a pointer to the file data
*****************************************************************************/
const void* CPVRTResourceFile::DataPtr() const
{
	return m_pData;
}

/*!***************************************************************************
@Function			Close
@Description		Closes the file
*****************************************************************************/
void CPVRTResourceFile::Close()
{
	if (m_bOpen)
	{
		if (!m_bMemoryFile && s_pReleaseFileFunc)
		{
			s_pReleaseFileFunc(m_Handle);
		}

		m_bMemoryFile = false;
		m_bOpen = false;
		m_pData = 0;
		m_Size = 0;
	}
}

/****************************************************************************
** class CPVRTMemoryFileSystem
****************************************************************************/
CPVRTMemoryFileSystem::CAtExit CPVRTMemoryFileSystem::s_AtExit;
CPVRTMemoryFileSystem::SFileInfo* CPVRTMemoryFileSystem::s_pFileInfo = 0;
int CPVRTMemoryFileSystem::s_i32Capacity = 0;
int CPVRTMemoryFileSystem::s_i32NumFiles = 0;

/*!***************************************************************************
@Function		Destructor
@Description	Destructor of CAtExit class. Workaround for platforms that
		        don't support the atexit() function. This deletes any memory
				file system data.
*****************************************************************************/
CPVRTMemoryFileSystem::CAtExit::~CAtExit()
{
	for (int i = 0; i < CPVRTMemoryFileSystem::s_i32NumFiles; ++i)
	{
		if (CPVRTMemoryFileSystem::s_pFileInfo[i].bAllocated)
		{
			delete [] (char*)CPVRTMemoryFileSystem::s_pFileInfo[i].pszFilename;
			delete [] (char*)CPVRTMemoryFileSystem::s_pFileInfo[i].pBuffer;
		}
	}
	delete [] CPVRTMemoryFileSystem::s_pFileInfo;
}

CPVRTMemoryFileSystem::CPVRTMemoryFileSystem(const char* pszFilename, const void* pBuffer, size_t Size, bool bCopy)
{
	RegisterMemoryFile(pszFilename, pBuffer, Size, bCopy);
}

/*!***************************************************************************
@Function		RegisterMemoryFile
@Input			pszFilename		Name of file to register
@Input			pBuffer			Pointer to file data
@Input			Size			File size
@Input			bCopy			Name and data should be copied?
@Description	Registers a block of memory as a file that can be looked up
				by name.
*****************************************************************************/
void CPVRTMemoryFileSystem::RegisterMemoryFile(const char* pszFilename, const void* pBuffer, size_t Size, bool bCopy)
{
	if (s_i32NumFiles == s_i32Capacity)
	{
		SFileInfo* pFileInfo = new SFileInfo[s_i32Capacity + 10];
		memcpy(pFileInfo, s_pFileInfo, sizeof(SFileInfo) * s_i32Capacity);
		delete [] s_pFileInfo;
		s_pFileInfo = pFileInfo;
		s_i32Capacity += 10;
	}

	s_pFileInfo[s_i32NumFiles].pszFilename = pszFilename;
	s_pFileInfo[s_i32NumFiles].pBuffer = pBuffer;
	if (bCopy)
	{
		char* pszNewFilename = new char[strlen(pszFilename) + 1];
		strcpy(pszNewFilename, pszFilename);
		s_pFileInfo[s_i32NumFiles].pszFilename = pszNewFilename;

		void* pszNewBuffer = new char[Size];
		memcpy(pszNewBuffer, pBuffer, Size);
		s_pFileInfo[s_i32NumFiles].pBuffer = pszNewBuffer;
	}
	s_pFileInfo[s_i32NumFiles].Size = Size;
	s_pFileInfo[s_i32NumFiles].bAllocated = bCopy;
	++s_i32NumFiles;
}

/*!***************************************************************************
@Function		GetFile
@Input			pszFilename		Name of file to open
@Output			ppBuffer		Pointer to file data
@Output			pSize			File size
@Return			true if the file was found in memory, false otherwise
@Description	Looks up a file in the memory file system by name. Returns a
				pointer to the file data as well as its size on success.
*****************************************************************************/
bool CPVRTMemoryFileSystem::GetFile(const char* pszFilename, const void** ppBuffer, size_t* pSize)
{
	for (int i = 0; i < s_i32NumFiles; ++i)
	{
		if (strcmp(s_pFileInfo[i].pszFilename, pszFilename) == 0)
		{
			if (ppBuffer) *ppBuffer = s_pFileInfo[i].pBuffer;
			if (pSize) *pSize = s_pFileInfo[i].Size;
			return true;
		}
	}
	return false;
}

/*!***************************************************************************
@Function		GetNumFiles
@Return			The number of registered files
@Description	Getter for the number of registered files
*****************************************************************************/
int CPVRTMemoryFileSystem::GetNumFiles()
{
	return s_i32NumFiles;
}

/*!***************************************************************************
@Function		GetFilename
@Input			i32Index		Index of file
@Return			A pointer to the filename of the requested file
@Description	Looks up a file in the memory file system by name. Returns a
				pointer to the file data as well as its size on success.
*****************************************************************************/
const char* CPVRTMemoryFileSystem::GetFilename(int i32Index)
{
	if (i32Index < 0 || i32Index > s_i32NumFiles) return 0;

	return s_pFileInfo[i32Index].pszFilename;
}


/*****************************************************************************
 End of file (PVRTResourceFile.cpp)
*****************************************************************************/