#pragma once
#include "..\stdafx.h"
#include "..\..\Common\XmlUtils.h"
#include "windows.h"

class CFile 
{
public:
	CFile() 
	{
		m_hFileHandle = NULL;
		m_lFileSize = 0;
		m_lFilePosition = 0;
	}

	virtual ~CFile()
	{
		CloseFile();
	}

	virtual HRESULT OpenFile(CString FileName)
	{	
		CloseFile();

		HRESULT hRes = S_OK;
		DWORD AccessMode = GENERIC_READ;
		DWORD ShareMode = FILE_SHARE_READ;
		DWORD Disposition = OPEN_EXISTING;
		m_hFileHandle = ::CreateFile(FileName, AccessMode, ShareMode, NULL, Disposition, FILE_ATTRIBUTE_NORMAL, NULL);

		if (NULL == m_hFileHandle || INVALID_HANDLE_VALUE == m_hFileHandle)
			hRes = S_FALSE;
		else 
		{
			ULARGE_INTEGER nTempSize;
			nTempSize.LowPart = ::GetFileSize(m_hFileHandle, &nTempSize.HighPart);
			m_lFileSize = nTempSize.QuadPart;

			SetPosition(0);
		}

		return hRes;
	}

	virtual HRESULT OpenFileRW(CString FileName)
	{	
		CloseFile();

		HRESULT hRes = S_OK;
		DWORD AccessMode = GENERIC_READ | GENERIC_WRITE;
		DWORD ShareMode = FILE_SHARE_READ;
		DWORD Disposition = OPEN_EXISTING;
		m_hFileHandle = ::CreateFile(FileName, AccessMode, ShareMode, NULL, Disposition, 0, 0);

		if (NULL == m_hFileHandle || INVALID_HANDLE_VALUE == m_hFileHandle)
		{
			hRes = S_FALSE;
		}
		else 
		{
			ULARGE_INTEGER nTempSize;
			nTempSize.LowPart = ::GetFileSize(m_hFileHandle, &nTempSize.HighPart);
			m_lFileSize = nTempSize.QuadPart;

			SetPosition(0);
		}

		return hRes;
	}

	HRESULT ReadFile(BYTE* pData, DWORD nBytesToRead)
	{
		DWORD nBytesRead = 0;
		if(NULL == pData)
			return S_FALSE;

		if(m_hFileHandle && (pData))
		{	
			SetPosition(m_lFilePosition);
			::ReadFile(m_hFileHandle, pData, nBytesToRead, &nBytesRead, NULL);
			m_lFilePosition += nBytesRead; 
		}
		return S_OK;
	}

	HRESULT ReadFile2(BYTE* pData, DWORD nBytesToRead)
	{
		DWORD nBytesRead = 0;
		if(NULL == pData)
			return S_FALSE;

		if(m_hFileHandle && (pData))
		{	
			SetPosition(m_lFilePosition);
			::ReadFile(m_hFileHandle, pData, nBytesToRead, &nBytesRead, NULL);
			m_lFilePosition += nBytesRead; 

			for (size_t index = 0; index < nBytesToRead / 2; ++index)
			{
				BYTE temp = pData[index];
				pData[index] = pData[nBytesToRead - index - 1];
				pData[nBytesToRead - index - 1] = temp;
			}
		}
		return S_OK;
	}
	HRESULT ReadFile3(void* pData, DWORD nBytesToRead)
	{
		DWORD nBytesRead = 0;
		if(NULL == pData)
			return S_FALSE;

		if(m_hFileHandle && (pData))
		{	
			SetPosition(m_lFilePosition);
			::ReadFile(m_hFileHandle, pData, nBytesToRead, &nBytesRead, NULL);
			m_lFilePosition += nBytesRead; 
		}
		return S_OK;
	}

	HRESULT WriteFile(void* pData, DWORD nBytesToWrite)
	{
		if(m_hFileHandle)
		{	
			DWORD dwWritten = 0;
			::WriteFile(m_hFileHandle, pData, nBytesToWrite, &dwWritten, NULL);
			m_lFilePosition += nBytesToWrite; 
		}
		return S_OK;
	}

	HRESULT WriteFile2(void* pData, DWORD nBytesToWrite)
	{
		if(m_hFileHandle)
		{	
			BYTE* mem = new BYTE[nBytesToWrite];
			memcpy(mem, pData, nBytesToWrite);
			
			for (size_t index = 0; index < nBytesToWrite / 2; ++index)
			{
				BYTE temp = mem[index];
				mem[index] = mem[nBytesToWrite - index - 1];
				mem[nBytesToWrite - index - 1] = temp;
			}
			
			DWORD dwWritten = 0;
			::WriteFile(m_hFileHandle, (void*)mem, nBytesToWrite, &dwWritten, NULL);
			m_lFilePosition += nBytesToWrite; 
			RELEASEARRAYOBJECTS(mem);
		}
		return S_OK;
	}

	HRESULT CreateFile(CString strFileName)
	{
		CloseFile();
		DWORD AccessMode = GENERIC_WRITE;
		DWORD ShareMode = FILE_SHARE_WRITE;
		DWORD Disposition = CREATE_ALWAYS;
		m_hFileHandle = ::CreateFile(strFileName, AccessMode, ShareMode, NULL, Disposition, FILE_ATTRIBUTE_NORMAL, NULL);
		return SetPosition(0);
	}
	HRESULT SetPosition( ULONG64 nPos )
	{	
		if (m_hFileHandle && nPos < (ULONG)m_lFileSize)
		{
			LARGE_INTEGER nTempPos;
			nTempPos.QuadPart = nPos;
			::SetFilePointer(m_hFileHandle, nTempPos.LowPart, &nTempPos.HighPart, FILE_BEGIN);
			m_lFilePosition = nPos;
			return S_OK;
		}
		else 
		{
			return (INVALID_HANDLE_VALUE == m_hFileHandle) ? S_FALSE : S_OK;
		}
	}
	LONG64  GetPosition()
	{
		return m_lFilePosition;
	}
	HRESULT SkipBytes(ULONG64 nCount)
	{
		return SetPosition(m_lFilePosition + nCount);
	}

	HRESULT CloseFile()
	{
		m_lFileSize = 0;
		m_lFilePosition = 0;
		RELEASEHANDLE(m_hFileHandle);
		return S_OK;
	}

	ULONG64 GetFileSize()
	{
		return m_lFileSize;
	}

	HRESULT WriteReserved(DWORD dwCount)
	{
		BYTE* buf = new BYTE[dwCount];
		memset(buf, 0, (size_t)dwCount);
		HRESULT hr = WriteFile(buf, dwCount);
		RELEASEARRAYOBJECTS(buf);
		return hr;
	}
	HRESULT WriteReserved2(DWORD dwCount)
	{
		BYTE* buf = new BYTE[dwCount];
		memset(buf, 0xFF, (size_t)dwCount);
		HRESULT hr = WriteFile(buf, dwCount);
		RELEASEARRAYOBJECTS(buf);
		return hr;
	}
	HRESULT WriteReservedTo(DWORD dwPoint)
	{
		if (m_lFilePosition >= dwPoint)
			return S_OK;

		DWORD dwCount = dwPoint - (DWORD)m_lFilePosition;
		BYTE* buf = new BYTE[dwCount];
		memset(buf, 0, (size_t)dwCount);
		HRESULT hr = WriteFile(buf, dwCount);
		RELEASEARRAYOBJECTS(buf);
		return hr;
	}
	HRESULT SkipReservedTo(DWORD dwPoint)
	{
		if (m_lFilePosition >= dwPoint)
			return S_OK;

		DWORD dwCount = dwPoint - (DWORD)m_lFilePosition;
		return SkipBytes(dwCount);
	}

	LONG GetProgress()
	{
		if (0 >= m_lFileSize)
			return -1;

		double dVal = (double)(100 * m_lFilePosition);
		LONG lProgress = (LONG)(dVal / m_lFileSize);
		return lProgress;
	}

	void WriteStringUTF8(CString& strXml)
	{
        int nLength = strXml.GetLength();

		CStringA saStr;
		
#ifdef UNICODE
		// Encoding Unicode to UTF-8
		WideCharToMultiByte(CP_UTF8, 0, strXml.GetBuffer(), nLength + 1, saStr.GetBuffer(nLength*3 + 1), nLength*3, NULL, NULL);
		saStr.ReleaseBuffer();    
#else
		wchar_t* pWStr = new wchar_t[nLength + 1];
		if (!pWStr)
			return;

		// set end string
		pWStr[nLength] = 0;

		// Encoding ASCII to Unicode
        MultiByteToWideChar(CP_ACP, 0, strXml, nLength, pWStr, nLength);

        int nLengthW = (int)wcslen(pWStr);

		// Encoding Unicode to UTF-8
        WideCharToMultiByte(CP_UTF8, 0, pWStr, nLengthW + 1, saStr.GetBuffer(nLengthW*3 + 1), nLengthW*3, NULL, NULL);
		saStr.ReleaseBuffer();

	    delete[] pWStr;
#endif
		
		WriteFile((void*)saStr.GetBuffer(), saStr.GetLength());
	}

protected:
	HANDLE m_hFileHandle;		
	LONG64 m_lFileSize;
	LONG64 m_lFilePosition;
};

namespace CDirectory
{
	static CString GetFolderName(CString strFolderPath)
	{
		int n1 = strFolderPath.ReverseFind('\\');
		if (-1 == n1)
			return _T("");

		return strFolderPath.Mid(n1 + 1);
	}
	static BOOL OpenFile(CString strFolderPath, CString strFileName, CFile* pFile)
	{
		CString strFile = strFolderPath + '\\' + strFileName;
		return (S_OK == pFile->OpenFile(strFile));
	}
	static BOOL CreateFile(CString strFolderPath, CString strFileName, CFile* pFile)
	{
		CString strFile = strFolderPath + '\\' + strFileName;
		return (S_OK == pFile->CreateFile(strFile));
	}
	static BOOL CreateDirectory(CString strFolderPathRoot, CString strFolderName)
	{
		CString strFolder = strFolderPathRoot + '\\' + strFolderName;
		return ::CreateDirectory(strFolder, NULL);
	}
	static BOOL CreateDirectory(CString strFolderPath)
	{
		return ::CreateDirectory(strFolderPath, NULL);
	}

	static BOOL MoveFile(CString strExists, CString strNew, LPPROGRESS_ROUTINE lpFunc, LPVOID lpData) 
	{
#if (_WIN32_WINNT >= 0x0500)
		return ::MoveFileWithProgress(strExists, strNew, lpFunc, lpData, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); 
#else
		return ::MoveFileEx(strExists, strNew, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); 
#endif
	}

	static BOOL CopyFile(CString strExists, CString strNew, LPPROGRESS_ROUTINE lpFunc, LPVOID lpData) 
	{
		DeleteFile(strNew);
		return ::CopyFileEx(strExists, strNew, lpFunc, lpData, FALSE, 0); 
	}

	static CString GetUnder(CString strFolderPathRoot, CString strFolderName)
	{
		CString strFolder = strFolderPathRoot + '\\' + strFolderName;
		return strFolder;
	}

	static CString GetFileName(CString strFullName)
	{
		int nStart = strFullName.ReverseFind('\\');
		CString strName = strFullName.Mid(nStart + 1);
		return strName;
	}

	static CString BYTEArrayToString(BYTE* arr, size_t nCount)
	{
		CString str;
		for (size_t index = 0; index < nCount; ++index)
		{
			if ('\0' != (char)(arr[index]))
				str += (char)(arr[index]);
		}
		if (str.GetLength() == 0)
			str = _T("0");
		return str;
	}

	static CStringW BYTEArrayToStringW(BYTE* arr, size_t nCount)
	{
		CStringW str;
		wchar_t* pArr = (wchar_t*)arr;
		size_t nCountNew = nCount / 2;
		for (size_t index = 0; index < nCountNew; ++index)
		{
			str += pArr[index];
		}
		if (str.GetLength() == 0)
			str = _T("0");
		return str;
	}

	static CString BYTEArrayToString2(USHORT* arr, size_t nCount)
	{
		CString str;
		for (size_t index = 0; index < nCount; ++index)
		{
			if ('\0' != (char)(arr[index]))
				str += (char)(arr[index]);
		}
		if (str.GetLength() == 0)
			str = _T("0");
		return str;
	}

	static CString ToString(DWORD val)
	{
		CString str = _T("");
		str.Format(_T("%d"), (LONG)val);
		return str;
	}

	static CString ToString(UINT64 val, bool bInit)
	{
		CString strCoarse = ToString((DWORD)(val >> 32));
		if (_T("0") != strCoarse)
		{
			return strCoarse + ToString((DWORD)val);
		}
		
		return ToString((DWORD)val);
	}

	static UINT64 GetUINT64(CString strVal)
	{
		UINT64 nRet = 0;
		int nLen = strVal.GetLength();
		while (nLen > 0)
		{
			int nDig = XmlUtils::GetDigit(strVal[0]);
			nRet *= 10;
			nRet += nDig;
			strVal.Delete(0);
			--nLen;
		}
		return nRet;
	}
	static UINT GetUINT(CString strVal)
	{
		return (UINT)GetUINT64(strVal);
	}

	static void WriteValueToNode(CString strName, DWORD value, XmlUtils::CXmlWriter* pWriter)
	{
		pWriter->WriteNodeBegin(strName);
		pWriter->WriteString(CDirectory::ToString(value));
		pWriter->WriteNodeEnd(strName);
	}
	static void WriteValueToNode(CString strName, LONG value, XmlUtils::CXmlWriter* pWriter)
	{
		pWriter->WriteNodeBegin(strName);
		CString strLONG = _T("");
		strLONG.Format(_T("%d"), value);
		pWriter->WriteString(strLONG);
		pWriter->WriteNodeEnd(strName);
	}
	static void WriteValueToNode(CString strName, CString value, XmlUtils::CXmlWriter* pWriter)
	{
		pWriter->WriteNodeBegin(strName);
		pWriter->WriteString(value);
		pWriter->WriteNodeEnd(strName);
	}
	static void WriteValueToNode(CString strName, WCHAR value, XmlUtils::CXmlWriter* pWriter)
	{
		CString str(value);

		pWriter->WriteNodeBegin(strName);
		pWriter->WriteString(str);
		pWriter->WriteNodeEnd(strName);
	}
	static void WriteValueToNode(CString strName, bool value, XmlUtils::CXmlWriter* pWriter)
	{
		pWriter->WriteNodeBegin(strName);
		CString str = (true == value) ? _T("1") : _T("0");
		pWriter->WriteString(str);
		pWriter->WriteNodeEnd(strName);
	}
	static void SaveToFile(CString strFileName, CString strXml)
	{
        int nLength = strXml.GetLength();

		CStringA saStr;
		
#ifdef UNICODE
		// Encoding Unicode to UTF-8
		WideCharToMultiByte(CP_UTF8, 0, strXml.GetBuffer(), nLength + 1, saStr.GetBuffer(nLength*3 + 1), nLength*3, NULL, NULL);
		saStr.ReleaseBuffer();    
#else
		wchar_t* pWStr = new wchar_t[nLength + 1];
		if (!pWStr)
			return;

		// set end string
		pWStr[nLength] = 0;

		// Encoding ASCII to Unicode
        MultiByteToWideChar(CP_ACP, 0, strXml, nLength, pWStr, nLength);

        int nLengthW = (int)wcslen(pWStr);

		// Encoding Unicode to UTF-8
        WideCharToMultiByte(CP_UTF8, 0, pWStr, nLengthW + 1, saStr.GetBuffer(nLengthW*3 + 1), nLengthW*3, NULL, NULL);
		saStr.ReleaseBuffer();

	    delete[] pWStr;
#endif
		
		CFile oFile;
		oFile.CreateFile(strFileName);
		oFile.WriteFile((void*)saStr.GetBuffer(), saStr.GetLength());
		oFile.CloseFile();
	}
}


