#include "c_api/helpers.h"

#include <cstring>

#include "c_api/lbug.h"

namespace {
thread_local std::string lastCAPIErrorMessage;
}

#ifdef _WIN32
const uint64_t NS_TO_SEC = 10000000ULL;
const uint64_t SEC_TO_UNIX_EPOCH = 11644473600ULL;

time_t convertTmToTime(struct tm tm) {
    SYSTEMTIME st;
    st.wYear = tm.tm_year + 1900;
    st.wMonth = tm.tm_mon + 1;
    st.wDay = tm.tm_mday;
    st.wHour = tm.tm_hour;
    st.wMinute = tm.tm_min;
    st.wSecond = tm.tm_sec;
    st.wMilliseconds = 0;
    FILETIME ft;
    if (!SystemTimeToFileTime(&st, &ft)) {
        return -1;
    }
    ULARGE_INTEGER ull;
    ull.LowPart = ft.dwLowDateTime;
    ull.HighPart = ft.dwHighDateTime;
    return static_cast<time_t>((ull.QuadPart / NS_TO_SEC) - SEC_TO_UNIX_EPOCH);
}

int32_t convertTimeToTm(time_t time, struct tm* out_tm) {
    ULARGE_INTEGER ull;
    ull.QuadPart = (time + SEC_TO_UNIX_EPOCH) * NS_TO_SEC;
    FILETIME ft;
    ft.dwLowDateTime = ull.LowPart;
    ft.dwHighDateTime = ull.HighPart;
    SYSTEMTIME st;
    if (!FileTimeToSystemTime(&ft, &st)) {
        return -1;
    }
    out_tm->tm_year = st.wYear - 1900;
    out_tm->tm_mon = st.wMonth - 1;
    out_tm->tm_mday = st.wDay;
    out_tm->tm_hour = st.wHour;
    out_tm->tm_min = st.wMinute;
    out_tm->tm_sec = st.wSecond;
    return 0;
}
#endif

void setLastCAPIErrorMessage(const std::string& message) {
    lastCAPIErrorMessage = message;
}

void clearLastCAPIErrorMessage() {
    lastCAPIErrorMessage.clear();
}

char* takeLastCAPIErrorMessage() {
    if (lastCAPIErrorMessage.empty()) {
        return nullptr;
    }
    auto* message = convertToOwnedCString(lastCAPIErrorMessage);
    lastCAPIErrorMessage.clear();
    return message;
}

char* convertToOwnedCString(const std::string& str) {
    size_t src_len = str.size();
    auto* c_str = (char*)malloc(sizeof(char) * (src_len + 1));
    memcpy(c_str, str.c_str(), src_len);
    c_str[src_len] = '\0';
    return c_str;
}

char* lbug_get_last_error() {
    return takeLastCAPIErrorMessage();
}
