// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

#ifdef WIN32

#include <perspective/first.h>
#include <perspective/base.h>
#include <perspective/raw_types.h>
#include <perspective/storage.h>
#include <perspective/raii.h>
#include <perspective/defaults.h>
#include <perspective/compat.h>
#include <perspective/utils.h>
#include <iostream>
#include <assert.h>
#include <csignal>
#include <iostream>
#include <map>
#include <sstream>
#include <vector>
#include <csignal>

namespace perspective {

t_lstore::t_lstore(const t_lstore_recipe& a) :
    m_dirname(a.m_dirname),
    m_colname(a.m_colname),
    m_base(0),
    m_fd(0),
    m_capacity(a.m_capacity),
    m_size(0),
    m_alignment(a.m_alignment),
    m_fflags(a.m_fflags),
    m_fmode(a.m_fmode),
    m_creation_disposition(a.m_creation_disposition),
    m_mprot(a.m_mprot),
    m_mflags(a.m_mflags),
    m_backing_store(a.m_backing_store),
    m_init(false),
    m_resize_factor(1.3),
    m_version(0),
    m_from_recipe(a.m_from_recipe) {
    if (m_from_recipe) {
        m_fname = a.m_fname;
        return;
    }

    if (m_backing_store == BACKING_STORE_DISK) {
        std::stringstream ss;
        ss << a.m_dirname << "\\"
           << "_col_" << a.m_colname << "_" << this;
        m_fname = unique_path(ss.str());
    }
}

t_handle
t_lstore::create_file() {
    t_handle rval = CreateFile(
        m_fname.c_str(),
        m_fflags,
        m_fmode,
        0, // security
        m_creation_disposition,
        FILE_ATTRIBUTE_NORMAL,
        0 // template file
    );

    PSP_VERBOSE_ASSERT(rval != INVALID_HANDLE_VALUE, "Error opening file");

    if (m_from_recipe) {
        return rval;
    }

    LARGE_INTEGER sz;
    sz.QuadPart = capacity();

    auto rb = SetFilePointerEx(rval, sz, 0, FILE_BEGIN);
    PSP_VERBOSE_ASSERT(rb, "Error setting fpointer");

    rb = SetEndOfFile(rval);
    PSP_VERBOSE_ASSERT(rb, "Error setting eof");

    return rval;
}

void*
t_lstore::create_mapping() {
    std::pair<std::uint32_t, std::uint32_t> capacity = capacity_pair();

    m_winmapping_handle = CreateFileMapping(
        m_fd,
        0, // default security
        m_mprot,
        capacity.first,
        capacity.second,
        0 // anonymous mapping
    );

    PSP_VERBOSE_ASSERT(m_winmapping_handle != 0, "Error creating filemapping");

    void* ptr = MapViewOfFile(
        m_winmapping_handle,
        m_mflags,
        0, // 0 offset
        0, // 0 offset
        0  // entire file
    );

    PSP_VERBOSE_ASSERT(ptr != 0, "Error mapping file");

    return ptr;
}

void
t_lstore::resize_mapping(t_uindex cap_new) {
    flush_mapping(m_base, capacity());
    destroy_mapping();

    // TODO error handling
    CloseHandle(m_winmapping_handle);

    LARGE_INTEGER sz;
    sz.QuadPart = cap_new;

    auto rb = SetFilePointerEx(m_fd, sz, 0, FILE_BEGIN);
    PSP_VERBOSE_ASSERT(rb, "Error setting fpointer");

    rb = SetEndOfFile(m_fd);
    PSP_VERBOSE_ASSERT(rb, "Error setting eof");

    m_capacity = cap_new;
    m_base = create_mapping();
}

void
t_lstore::destroy_mapping() {
    auto rc = UnmapViewOfFile(m_base);
    PSP_VERBOSE_ASSERT(rc, "Error unmapping view");
    m_base = 0;
}

void
t_lstore::freeze_impl() {
    DWORD dwOld;
    if (VirtualProtect(
            (LPVOID)this,
            static_cast<size_t>(get_page_size()),
            PAGE_READONLY,
            &dwOld
        )
        == 0) {
        std::cout << "Virtual protect failed addr => " << this
                  << " error code => " << GetLastError() << "\n";
        PSP_VERBOSE_ASSERT(false, "virtual protect failed");
    }
}

void
t_lstore::unfreeze_impl() {
    DWORD dwOld;
    if (VirtualProtect(
            (LPVOID)this,
            static_cast<size_t>(get_page_size()),
            PAGE_READWRITE,
            &dwOld
        )
        == 0) {
        std::cout << "Virtual protect failed addr => " << this
                  << " error code => " << GetLastError() << "\n";
        PSP_VERBOSE_ASSERT(false, "virtual protect failed");
    }
}

} // end namespace perspective

#endif
