#include "Win32AssetManager.h"
#include "Profiling.h"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

void Win32AssetManager::create(const char* asset_root_dir)
{
    ZoneScoped;
    static Win32AssetManager mgr;
    mgr.m_asset_root = asset_root_dir;
    AssetManager::ptr = &mgr;
}

bool Win32AssetManager::loadFile(const char* path, FileBuffer* p_file_buffer)
{
    ZoneScoped;
    // TODO(Kevin): We could totally optimize this via a custom allocator
    char _cp[MAX_PATH];
    strncpy(_cp, m_asset_root, MAX_PATH);
    strncat(_cp, "\\", MAX_PATH);
    strncat(_cp, path, MAX_PATH);

    WIN32_FILE_ATTRIBUTE_DATA attribs;
    if (!GetFileAttributesExA(_cp, GetFileExInfoStandard, &attribs)) {
        return false;
    }
    size_t size = static_cast<size_t>(attribs.nFileSizeHigh) << 32 | attribs.nFileSizeLow;
    void* data = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    p_file_buffer->data = data;
    p_file_buffer->size = size;
    p_file_buffer->internal = nullptr;

    HANDLE file =
        CreateFileA(_cp, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (!file) {
        VirtualFree(data, 0, MEM_RELEASE);
        return false;
    }
    char* dst = reinterpret_cast<char*>(data);
    while (size >= UINT32_MAX) {
        DWORD bytes_read = 0;
        if (!ReadFile(file, dst, UINT32_MAX, &bytes_read, nullptr)) {
            // Error occured
            CloseHandle(file);
            VirtualFree(data, 0, MEM_RELEASE);
            return false;
        }
        size -= bytes_read;
        dst += bytes_read;
    }
    if (size > 0) {
        DWORD bytes_read = 0;
        if (!ReadFile(file, dst, size, &bytes_read, nullptr)) {
            // Error occured
            CloseHandle(file);
            VirtualFree(data, 0, MEM_RELEASE);
            return false;
        }
    }

    CloseHandle(file);
    return true;
}

void Win32AssetManager::releaseFileBuffer(FileBuffer& fb)
{
    ZoneScoped;
    void* data = const_cast<void*>(fb.data);
    VirtualFree(data, 0, MEM_RELEASE);
    fb.data = nullptr;
    fb.size = 0;
}

bool Win32AssetManager::writeFile(const char* path, const void* data, size_t size)
{
    ZoneScoped;
    char _cp[MAX_PATH];
    strncpy(_cp, m_asset_root, MAX_PATH);
    strncat(_cp, "\\", MAX_PATH);
    strncat(_cp, path, MAX_PATH);

    HANDLE file =
        CreateFileA(_cp, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (!file) {
        return false;
    }

    const char* at = reinterpret_cast<const char*>(data);
    while (size >= UINT32_MAX) {
        DWORD bytes_written = 0;
        if (!WriteFile(file, at, UINT32_MAX, &bytes_written, nullptr)) {
            CloseHandle(file);
            return false;
        }
        size -= bytes_written;
        at += bytes_written;
    }
    if (size > 0) {
        DWORD bytes_written = 0;
        if (!WriteFile(file, at, size, &bytes_written, nullptr)) {
            CloseHandle(file);
            return false;
        }
    }
    CloseHandle(file);
    return true;
}

bool Win32AssetManager::writeFile(const char* path, const FileBuffer& file_buffer)
{
    ZoneScoped;
    return writeFile(path, file_buffer.data, file_buffer.size);
}

Win32AssetManager::FileHandle Win32AssetManager::createFile(const char* path)
{
    ZoneScoped;
    char _cp[MAX_PATH];
    strncpy(_cp, m_asset_root, MAX_PATH);
    strncat(_cp, "\\", MAX_PATH);
    strncat(_cp, path, MAX_PATH);

    HANDLE h = CreateFileA(_cp, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (!h || h == INVALID_HANDLE_VALUE)
        return nullptr;
    return (h);
}

bool Win32AssetManager::writeToFile(FileHandle _file, const void* data, size_t size)
{
    ZoneScoped;
    HANDLE file = (HANDLE)_file;
    const char* at = reinterpret_cast<const char*>(data);
    while (size >= UINT32_MAX) {
        DWORD bytes_written = 0;
        if (!WriteFile(file, at, UINT32_MAX, &bytes_written, nullptr)) {
            CloseHandle(file);
            return false;
        }
        size -= bytes_written;
        at += bytes_written;
    }
    if (size > 0) {
        DWORD bytes_written = 0;
        if (!WriteFile(file, at, size, &bytes_written, nullptr)) {
            CloseHandle(file);
            return false;
        }
    }

    return true;
}

bool Win32AssetManager::writeToFile(FileHandle file, const FileBuffer& file_buffer)
{
    ZoneScoped;
    return writeToFile(file, file_buffer.data, file_buffer.size);
}

void Win32AssetManager::closeFile(FileHandle file)
{
    ZoneScoped;
    CloseHandle((HANDLE)file);
}