#ifdef _WIN32

#include <FileIO/FileIO.hpp>
#include <Utilities/Utilities.hpp>

FileIO::~FileIO()
{
    if (hFile != NULL)
    {
        CloseHandle(hFile);
    }
}

bool FileIO::create(std::string path)
{
#ifdef DEBUG_BUILD
    if (hFile != NULL)
    {
        crash("FileIO::create called when a file is already open!");
    }
#endif

    hFile = CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        hFile = NULL;
        return false;
    }

    return true;
}

bool FileIO::open(std::string path)
{
#ifdef DEBUG_BUILD
    if (hFile != NULL)
    {
        crash("FileIO::open called when a file is already open!");
    }
#endif

    hFile = CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        hFile = NULL;
        return false;
    }

    return true;
}

bool FileIO::lock()
{
#ifdef DEBUG_BUILD
    if (hFile == NULL)
    {
        crash("FileIO::lock called but no file is open!");
    }
#endif

    OVERLAPPED overlapped{};
    return LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
}

void FileIO::close()
{
    if (hFile != NULL)
    {
        CloseHandle(hFile);
        hFile = NULL;
    }
}

bool FileIO::read(size_t offset, void *data, size_t size)
{
    DWORD bytesRead;
    OVERLAPPED overlapped{};
    overlapped.Offset = static_cast<DWORD>(offset);
    overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);

    if (!ReadFile(hFile, data, static_cast<DWORD>(size), &bytesRead, &overlapped) || bytesRead != size)
    {
        return false;
    }

    return true;
}

bool FileIO::write(size_t offset, const void *data, size_t size)
{
    DWORD bytesWritten;
    OVERLAPPED overlapped{};
    overlapped.Offset = static_cast<DWORD>(offset);
    overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);

    if (!WriteFile(hFile, data, static_cast<DWORD>(size), &bytesWritten, &overlapped) || bytesWritten != size)
    {
        return false;
    }

    return true;
}

bool FileIO::sync()
{
    return FlushFileBuffers(hFile) != 0;
}

bool FileIO::isOpen()
{
    return hFile != NULL;
}

size_t FileIO::size()
{
    LARGE_INTEGER size;
    GetFileSizeEx(hFile, &size);
    return static_cast<size_t>(size.QuadPart);
}

#endif