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


#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <Shlwapi.h>
#include "cFilePackage.h"

bool cFilePackage::TransFilename(const char* instr, char* outstr, int outsize)
{
	if ( instr == NULL )
		return false;

	char buf[MAX_PATH];
	int i = 0;
	int j = 0;
	while ( instr[i] ) {
		buf[j] = ::tolower(instr[i]);

		switch (buf[j])
		{
			// "/"
		case 47:			
			buf[j] = 92;
			if ( instr[i+1] == 47 )
				i++;
			break;

		case 92:			
			if ( instr[i+1] == 92 )
				i++;
			break;
		}
		i++;
		j++;
	}
	buf[j] = 0;
	strncpy(outstr, buf, outsize);
	outstr[outsize - 1] = 0;
	return true;
}

cFilePackage::cFilePackage()
{	
	m_table.clear();
	m_fileHandle.clear();	
	//m_currentIter = m_table.end();
	memset( m_dirStr, 0, sizeof(m_dirStr) );
}

cFilePackage::~cFilePackage()
{
	Destroy();
}

void cFilePackage::Destroy()
{
	/*
	for ( set<FileDbIndex*, FileDbIndex>::iterator iter = m_table.begin(); iter != m_table.end(); iter++ ) {
		if ( (*iter)->fileNameStr ) {
			delete [] (*iter)->fileNameStr;
			(*iter)->fileNameStr = NULL;
		}
	}
	*/

	// 刪除暫存檔
	for ( vector<string>::iterator iter = m_tempFile.begin(); iter != m_tempFile.end(); iter++ )
	{
		DeleteFile(iter->c_str());
	}

	for ( vector<HANDLE>::iterator iter = m_fileHandle.begin(); iter != m_fileHandle.end(); iter++ ) {
		CloseHandle( *iter );
	}

	m_table.clear();
	m_fileHandle.clear();	
	//m_currentIter = m_table.end();
	memset( m_dirStr, 0, sizeof(m_dirStr) );
}

bool cFilePackage::FileExists(const char* filename)
{
	if ( Find(filename) ) {
		return true;
	} else {
		const char* pathFilename = GetFullNameStr(filename);
		if ( ::PathFileExists(pathFilename) )
			return true;
	}
	return false;
}

cFileObj* cFilePackage::GetFileObj(const char *filename)
{
	cFileObj *pFileObj = NULL;
	if ( filename )
	{
		int start = 0;
		while (filename[start] && (filename[start] == 92 || filename[start] == 47))
			start++;

		FileDbIndex* pFileIndex = Find(filename + start);
		if ( pFileIndex )
		{
			switch ( pFileIndex->m_fileType )
			{
			case FILE_TYPE_GENERAL:
				pFileObj = new cFileObj();
				break;
			case FILE_TYPE_IMAGE:
				pFileObj = new cImgFile();
				break;
			default:
				return NULL;
			}

			bool ret = true;
			if ( pFileIndex->m_isFromDB )
			{
				SetFilePointer(pFileIndex->m_hFile,pFileIndex->m_offset,0,FILE_BEGIN);
				if ( !pFileObj->LoadFromData(pFileIndex->m_hFile) )
					ret = false;
			}
			else
			{
				if ( !pFileObj->LoadFromFile(m_dirStr, filename) )
					ret = false;
			}

			if ( !ret )
			{
				delete pFileObj;
				pFileObj = NULL;
			}
		}
	}
	return pFileObj;
}

IRuStream *cFilePackage::GetIRuStream(const char *keyStr)
{
	cFileObj *pFileObj;

	if ( keyStr == NULL )
		return NULL;

	// 試著開啟真實路徑下檔案
	if ( IsImageFile( keyStr ) )
	{
		pFileObj = new cImgFile();
		if ( pFileObj->LoadFromFile(m_dirStr, keyStr) )
			return pFileObj;
        delete pFileObj;
	}
	else
	{
		pFileObj = new cFileObj();
		if ( pFileObj->LoadFromFile(m_dirStr, keyStr) )
			return pFileObj;
		delete pFileObj;
	}

	if ( (pFileObj = GetFileObj(keyStr)) != NULL )
	{
		if ( !pFileObj->ConvertFormat( COMPRESS_FMT_NORMAL ) )
		{
			delete pFileObj;
			pFileObj = NULL;
		}
	}

	return pFileObj;
}

BOOL cFilePackage::GetCRuTempFileStream( const char *keyStr, CRuFileStream& tempFileStream )
{
	BOOL resultBool = FALSE;
	IRuStream* fileStream = GetIRuStream(keyStr);
	if ( fileStream )
	{
		char tempFilename[512];
		sprintf_s(tempFilename, "temp\\%s", keyStr);

		CreatePath(tempFilename);

		if ( tempFileStream.Create(tempFilename) == ruSEC_OK )
		{
			tempFileStream.Write(fileStream->OpenStreamMapping(), fileStream->GetStreamSize());
			tempFileStream.Seek( 0 , ruSSM_Begin );

			m_tempFile.push_back(tempFilename);			

			resultBool = TRUE;
		}

		delete fileStream;
	}
	return resultBool;
}

bool cFilePackage::AddFile(const TCHAR *fileNameStr, void *pSpecialData, CompressFormat format)
{
	if (fileNameStr == NULL) 
		return false;

	HANDLE hFile;
	if ( (hFile = CreateFile( GetFullNameStr(fileNameStr) , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ) ) == INVALID_HANDLE_VALUE ) {
		return false;
	}

	FileDbIndex fileInfo;
	fileInfo.m_isFromDB		= false;
	fileInfo.m_hFile		= NULL;
	GetFileTime( hFile, NULL, NULL, &fileInfo.m_fileTime );
	CloseHandle( hFile );

	if ( IsImageFile( fileNameStr ) )
		fileInfo.m_fileType = FILE_TYPE_IMAGE;
	else
		fileInfo.m_fileType = FILE_TYPE_GENERAL;

	return AddToTable(fileNameStr, fileInfo);
}

// ----------------------------------------------------------------------------
// 讀取檔頭資料
bool cFilePackage::LoadFromFile( const TCHAR *fileNameStr )
{
	HANDLE hFile = CreateFile( fileNameStr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
	if ( hFile == INVALID_HANDLE_VALUE ) return false;

	DWORD magic, readBytesOut;
	ReadFile( hFile, &magic, sizeof(DWORD), &readBytesOut, NULL );
	if ( magic != FILEPACKAGE_MAGIC ) {
		CloseHandle( hFile );
		return false;
	}

	bool result = false;

	// 讀取總筆數
	INT count;
	ReadFile( hFile, &count, sizeof(DWORD), &readBytesOut, NULL );
	if ( count > 0 )
	{
		// 讀入TABLE檔頭
		FileDataBaseHeader* header = new FileDataBaseHeader[count];
		ReadFile( hFile, header, sizeof(FileDataBaseHeader) * count, &readBytesOut, NULL );

		// 字串長度陣列
		INT* textLength = new INT[count];
		ReadFile( hFile, textLength, sizeof(INT) * count, &readBytesOut, NULL );

		// 字串內容大小
		INT textSize;
		ReadFile( hFile, &textSize, sizeof(INT), &readBytesOut, NULL );

		if ( textSize > 0 ) {
			// 字串內容
			char* text = new char[textSize];
			ReadFile( hFile, text, sizeof(char) * textSize, &readBytesOut, NULL );

			// 寫入表格內
			FileDbIndex info;
			unsigned int pos = 0;
			for ( int i = 0; i < count; i++ ) {
				info.m_hFile		= hFile;
				info.m_isFromDB		= true;
				info.m_fileType		= header[i].m_fileType;
				info.m_fileTime		= header[i].m_fileTime;
				info.m_offset		= header[i].m_offset;

				AddToTable(text + pos, info);
				pos += textLength[i] + 1;
			}

			delete [] text;
			result = true;
		}
		
		delete [] textLength;
		delete [] header;
	}	

	if ( result )
		m_fileHandle.push_back( hFile );

	return result;
}

int cFilePackage::SaveToFile( const TCHAR *fileNameStr )
{
	if ( m_table.empty() ) return -1;

	HANDLE hFile = CreateFile( fileNameStr, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
	if ( hFile == INVALID_HANDLE_VALUE ) return -2;
	
	DWORD writeBytesOut;
	DWORD magic = FILEPACKAGE_MAGIC;
	WriteFile( hFile, &magic, sizeof(DWORD), &writeBytesOut, NULL );		// 寫入檔案檢查碼
	
	int count = 0;
	/*
	for ( set<FileDbIndex*, FileDbIndex>::iterator iter = m_table.begin(); iter != m_table.end(); iter++ ) {
		cFileObj *pFileObj = NULL;
		if ( GetFileObj( (*iter)->filename, &pFileObj ) ) {
			pFileObj->SaveData( hFile );
			count++;
			delete pFileObj;
			pFileObj = NULL;
		}
	}
	*/

	const int zero = 0;
	WriteFile( hFile, &zero, sizeof(int), &writeBytesOut, NULL );			// 區塊結束
	WriteFile( hFile, &count, sizeof(int), &writeBytesOut, NULL );
	CloseHandle( hFile );

	return count;
}

void cFilePackage::CreatePath(const char* filename)
{
	if ( filename == NULL )
		return;
	
	const char* beginFilename = filename;
	while ( true )
	{
		const char* lastPathIndex = strchr(beginFilename, '\\');
		if ( lastPathIndex == NULL )
			lastPathIndex = strchr(beginFilename, '/');

		if ( lastPathIndex )
		{
			char temp[512];
			memcpy(temp, filename, lastPathIndex - filename);
			temp[lastPathIndex - filename] = 0;

			if ( !PathIsDirectory( temp ) )
			{
				CreateDirectory( temp, NULL );
			}
			beginFilename = lastPathIndex + 1;
		}
		else
		{
			return;
		}
	}
}

/*
FileDbIndex *cFilePackage::GetFirstItem()
{
	if ( m_table.empty() ) return NULL;
	m_currentIter = m_table.begin();
	return *m_currentIter;
}

FileDbIndex *cFilePackage::GetNextItem()
{	
	if ( m_currentIter != m_table.end() ) m_currentIter++;
	if ( m_currentIter != m_table.end() ) {
		return *m_currentIter;
	}
	return NULL;
}
*/

void cFilePackage::SetDirectory( const TCHAR *dirStr )
{
	if ( dirStr )
	{
		_tcscpy( m_dirStr, dirStr );
		int len = (int)_tcslen( m_dirStr );
		if ( len > 0 && m_dirStr[len] != _T('\\') )
			_tcscat( m_dirStr, _T("\\") );
	}
}

bool cFilePackage::AddToTable(string filename, FileDbIndex &fileInfo)
{
	char buf[MAX_PATH];
	TransFilename(filename.c_str(), buf, MAX_PATH);
	m_table.insert( make_pair(buf, fileInfo) );
	return true;	
}

FileDbIndex *cFilePackage::Find(string filename)
{
	char buf[MAX_PATH];
	TransFilename(filename.c_str(), buf, MAX_PATH);
	map<string, FileDbIndex>::iterator iter = m_table.find(buf);
	if ( iter != m_table.end() )
		return &(iter->second);
	return NULL;
}

TCHAR *cFilePackage::GetFullNameStr( const TCHAR *fileNameStr )
{
	static TCHAR fullNameStr[MAX_PATH];
	if ( fileNameStr ) {
		_tcscpy( fullNameStr, m_dirStr );
		_tcscat( fullNameStr, fileNameStr );
		return fullNameStr;
	}
	return NULL;
}