#include "stdafx.h"
#ifdef _WIN32
#include <io.h>
#define USE_WINDOWS_API
#include <Windows.h>
#else
#include <unistd.h>
#endif

#if defined(_MSC_VER)
#include <crtdbg.h>
#else
#define _ASSERTE(expr)          ((void)0)
#endif

CFile::CFile() :
#ifdef USE_WINDOWS_API
m_hFile(INVALID_HANDLE_VALUE),
#else
m_hFile(NULL),
#endif
m_bCloseOnDelete(false)
{
}

CFile::CFile(void * hFile) :
m_hFile(hFile),
m_bCloseOnDelete(true)
{
    if (hFile == 0)
    {
        _ASSERTE(hFile != 0);
    }
}

CFile::CFile(const char * lpszFileName, uint32_t nOpenFlags) :
#ifdef USE_WINDOWS_API
m_hFile(INVALID_HANDLE_VALUE),
#else
m_hFile(NULL),
#endif
m_bCloseOnDelete(true)
{
    Open(lpszFileName, nOpenFlags);
}

CFile::~CFile()
{
#ifdef USE_WINDOWS_API
    if (m_hFile != INVALID_HANDLE_VALUE && m_bCloseOnDelete)
#else
    if (m_hFile != NULL && m_bCloseOnDelete)
#endif
    {
        Close();
}
}

bool CFile::Open(const char * lpszFileName, uint32_t nOpenFlags)
{
    if (!Close())
    {
        return false;
    }

    if (lpszFileName == NULL || strlen(lpszFileName) == 0)
    {
        return false;
    }

    m_bCloseOnDelete = true;
#ifdef USE_WINDOWS_API
    m_hFile = INVALID_HANDLE_VALUE;

    ULONG dwAccess = 0;
    switch (nOpenFlags & 3)
    {
    case modeRead:
        dwAccess = GENERIC_READ;
        break;
    case modeWrite:
        dwAccess = GENERIC_WRITE;
        break;
    case modeReadWrite:
        dwAccess = GENERIC_READ | GENERIC_WRITE;
        break;
    default:
        _ASSERTE(false);
    }

    // map share mode
    ULONG dwShareMode = 0;

    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    if ((nOpenFlags & shareDenyWrite) == shareDenyWrite) { dwShareMode &= ~FILE_SHARE_WRITE; }
    if ((nOpenFlags & shareDenyRead) == shareDenyRead)   { dwShareMode &= ~FILE_SHARE_READ; }
    if ((nOpenFlags & shareExclusive) == shareExclusive) { dwShareMode = 0; }

    // map modeNoInherit flag
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = (nOpenFlags & modeNoInherit) == 0;

    // map creation flags
    ULONG dwCreateFlag = OPEN_EXISTING;
    if (nOpenFlags & modeCreate)
    {
        dwCreateFlag = ((nOpenFlags & modeNoTruncate) != 0) ? OPEN_ALWAYS : CREATE_ALWAYS;
    }

    // attempt file creation
    HANDLE hFile = ::CreateFile(lpszFileName, dwAccess, dwShareMode, &sa, dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    { //#define ERROR_PATH_NOT_FOUND             3L
        //ULONG err = GetLastError();
        return false;
    }
    m_hFile = hFile;
#else
    if ((nOpenFlags & modeNoTruncate) == 0 && (nOpenFlags & CFileBase::modeCreate) == CFileBase::modeCreate)
    {
        CPath(lpszFileName).Delete();
    }
    if ((nOpenFlags & CFileBase::modeCreate) != CFileBase::modeCreate)
    {
        if (!CPath(lpszFileName).Exists())
        {
            return false;
        }
    }

    if ((nOpenFlags & CFileBase::modeCreate) == CFileBase::modeCreate)
    {
        CPath file(lpszFileName);
        if (!file.Exists())
        {
            FILE * fp = fopen(lpszFileName,"wb");
            if (fp)
            {
                fclose(fp);
            }
            if (!file.Exists())
            {
                return false;
            }
        }
    }

    if ((nOpenFlags & CFileBase::modeWrite) == CFileBase::modeWrite ||
        (nOpenFlags & CFileBase::modeReadWrite) == CFileBase::modeReadWrite)
    {
        m_hFile = fopen(lpszFileName, "rb+");
        if (m_hFile != NULL)
        {
            SeekToBegin();
        }
    }
    else if ((nOpenFlags & CFileBase::modeRead) == CFileBase::modeRead)
    {
        m_hFile = fopen(lpszFileName, "rb");
        if (m_hFile != NULL)
        {
            SeekToBegin();
        }
    }
    else
    {
        return false;
    }
#endif
    m_bCloseOnDelete = true;
    return true;
}

bool CFile::Close()
{
    bool bError = true;
#ifdef USE_WINDOWS_API
    if (m_hFile != INVALID_HANDLE_VALUE)
    {
        bError = !::CloseHandle(m_hFile);
    }
    m_hFile = INVALID_HANDLE_VALUE;
#else
    if (m_hFile != NULL)
    {
        fclose((FILE *)m_hFile);
        m_hFile = NULL;
    }
#endif
    m_bCloseOnDelete = false;
    return bError;
}

uint32_t CFile::SeekToEnd(void)
{
    return Seek(0, CFile::end);
}

void CFile::SeekToBegin(void)
{
    Seek(0, CFile::begin);
}

bool CFile::IsOpen(void) const
{
#ifdef USE_WINDOWS_API
    return m_hFile != INVALID_HANDLE_VALUE;
#else
    return m_hFile != NULL;
#endif
}

bool CFile::Flush()
{
#ifdef USE_WINDOWS_API
    if (m_hFile == INVALID_HANDLE_VALUE)
    {
        return true;
    }

    return ::FlushFileBuffers(m_hFile) != 0;
#else
    return fflush((FILE *)m_hFile) == 0;
#endif
}

bool CFile::Write(const void* lpBuf, uint32_t nCount)
{
    if (nCount == 0)
    {
        return true;     // avoid Win32 "null-write" option
    }

#ifdef USE_WINDOWS_API
    ULONG nWritten = 0;
    if (!::WriteFile(m_hFile, lpBuf, nCount, &nWritten, NULL))
    {
        return false;
    }

    if (nWritten != nCount)
    {
        // Win32s will not return an error all the time (usually DISK_FULL)
        return false;
    }
#else
    if (fwrite(lpBuf, 1, nCount, (FILE *)m_hFile) != nCount)
    {
        return false;
    }
#endif
    return true;
}

uint32_t CFile::Read(void* lpBuf, uint32_t nCount)
{
    if (nCount == 0)
    {
        return 0;   // avoid Win32 "null-read"
    }

#ifdef USE_WINDOWS_API
    DWORD dwRead = 0;
    if (!::ReadFile(m_hFile, lpBuf, nCount, &dwRead, NULL))
    {
        return 0;
    }
    return (uint32_t)dwRead;
#else
    uint32_t res = fread(lpBuf, sizeof(uint8_t), nCount, (FILE *)m_hFile);
    return res;
#endif
}

int32_t CFile::Seek(int32_t lOff, SeekPosition nFrom)
{
#ifdef USE_WINDOWS_API
    ULONG dwNew = ::SetFilePointer(m_hFile, lOff, NULL, (ULONG)nFrom);
    if (dwNew == (ULONG)-1)
    {
        return -1;
    }
    return dwNew;
#else
    if (m_hFile == NULL)
    {
        return -1;
    }
    int origin;

    switch (nFrom)
    {
    case begin: origin = SEEK_SET; break;
    case current: origin = SEEK_CUR; break;
    case end: origin = SEEK_END; break;
    default:
        return -1;
    }

    Flush();
    int res = fseek((FILE *)m_hFile, lOff, origin);
    return res;
#endif
}

uint32_t CFile::GetPosition() const
{
#ifdef USE_WINDOWS_API
    return ::SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
#else
    return (uint32_t)ftell((FILE *)m_hFile);
#endif
}

bool CFile::SetLength(uint32_t dwNewLen)
{
    Seek((int32_t)dwNewLen, begin);
    return SetEndOfFile();
}

uint32_t CFile::GetLength() const
{
#ifdef USE_WINDOWS_API
    return GetFileSize(m_hFile, 0);
#else
    uint32_t pos = GetPosition();
    fseek((FILE *)m_hFile, 0, SEEK_END);
    uint32_t FileSize = GetPosition();
    fseek((FILE *)m_hFile, (int32_t)pos, SEEK_SET);
    return FileSize;
#endif
}

bool CFile::SetEndOfFile()
{
#ifdef USE_WINDOWS_API
    return ::SetEndOfFile(m_hFile) != 0;
#else
    Flush();
#ifdef _WIN32
    return _chsize(_fileno((FILE *)m_hFile),GetPosition()) == 0;
#else
    return ftruncate(fileno((FILE *)m_hFile),GetPosition()) == 0;
#endif
#endif
}