#include "../include/tool.h"

#include <iostream>
#include <iomanip>
#include <vector>
#include <random>

#ifdef _WIN32
#include <Windows.h>
#elif defined(__linux__)
#include <locale>
#include <codecvt>
#include <unistd.h>
#include <limits.h>
#endif

#ifdef _WIN32

std::wstring Z::HResultToWString(HRESULT hr) {
    LPWSTR buffer = nullptr;

    DWORD flags =
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS;

    DWORD len = FormatMessageW(
        flags, nullptr, hr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPWSTR>(&buffer), 0, nullptr);

    // 如果是 HRESULT_FROM_WIN32(...) 形式，尝试取低 16 位 Win32 error code
    if (len == 0 && HRESULT_FACILITY(hr) == FACILITY_WIN32) {
        len = FormatMessageW(
            flags,
            nullptr,
            HRESULT_CODE(hr),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            reinterpret_cast<LPWSTR>(&buffer),
            0,
            nullptr
        );
    }

    if (len == 0) {
        wchar_t fallback[64];
        swprintf_s(fallback, L"Unknown HRESULT: 0x%08X", static_cast<unsigned>(hr));
        return fallback;
    }

    std::wstring message(buffer, len);
    LocalFree(buffer);

    return message;
}
#endif


namespace {
std::once_flag initflag{};
}

//region 字符串转换(UTF8 - WSTRING)
namespace Z {
#ifdef _WIN32
std::string wstring_to_u8(const std::wstring &wstr) {
    if (wstr.empty())return {};
    int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(),
                                  static_cast<int>(wstr.size()),
                                  nullptr, 0, nullptr, nullptr);
    if (len <= 0) throw std::runtime_error("Z::wstring_to_u8 compute length error.");

    std::string str(len, '\0');
    len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(),
                              static_cast<int>(wstr.size()),
                              str.data(), len, nullptr, nullptr);
    if (len <= 0)throw std::runtime_error("Z::wstring_to_u8 escape error.");
    return str;
}

std::wstring u8_to_wstring(const std::string &u8str) {
    if (u8str.empty())return {};
    int len = MultiByteToWideChar(CP_UTF8, 0, u8str.c_str(), -1, nullptr, 0);
    if (len <= 0) throw std::runtime_error("Z::u8_to_wstring compute length error.");

    std::wstring ws(len, L'\0');
    len = MultiByteToWideChar(CP_UTF8, 0, u8str.c_str(), -1, ws.data(), len);
    if (len <= 0) throw std::runtime_error("Z::u8_to_wstring escape error.");
    return ws;
}
#else
std::string wstring_to_u8(const std::wstring &wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
    return conv.to_bytes(wstr);
}

std::wstring u8_to_wstring(const std::string &u8str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
    return conv.from_bytes(str);
}
#endif

}

//region filesystem相关
namespace Z {
std::filesystem::path get_module_path() {
#if defined(_WIN32)
#if defined(UNICODE) || defined(_UNICODE)
    // Windows Unicode / wide char variant
    std::wstring buffer(MAX_PATH, L'\0');
    DWORD length = GetModuleFileNameW(
        nullptr,
        buffer.data(),
        static_cast<DWORD>(buffer.size())
    );
    if (length == 0) {
        throw std::runtime_error("GetModuleFileNameW failed");
    }
    // 如果路径长度刚好达到 buffer 大小，可能被截断，动态扩容
    while (length == buffer.size()) {
        buffer.resize(buffer.size() * 2);
        length = GetModuleFileNameW(
            nullptr,
            buffer.data(),
            static_cast<DWORD>(buffer.size())
        );
        if (length == 0) {
            throw std::runtime_error("GetModuleFileNameW failed");
        }
    }
    buffer.resize(length);
    return std::filesystem::path(buffer);

#else
    // Windows non-Unicode / narrow char variant
    std::string buffer(MAX_PATH, '\0');
    DWORD length = GetModuleFileNameA(
        nullptr,
        buffer.data(),
        static_cast<DWORD>(buffer.size())
    );
    if (length == 0) {
        throw std::runtime_error("GetModuleFileNameA failed");
    }
    // 如果路径长度刚好达到 buffer 大小，可能被截断，动态扩容
    while (length == buffer.size()) {
        buffer.resize(buffer.size() * 2);
        length = GetModuleFileNameA(
            nullptr,
            buffer.data(),
            static_cast<DWORD>(buffer.size())
        );
        if (length == 0) {
            throw std::runtime_error("GetModuleFileNameA failed");
        }
    }

    buffer.resize(length);
    return std::filesystem::path(buffer);

#endif
#elif defined(__linux__)
    // Linux / GCC variant
    std::vector<char> buffer(PATH_MAX);
    while (true) {
        ssize_t length = readlink(
            "/proc/self/exe",
            buffer.data(),
            buffer.size()
        );
        if (length == -1) {
            throw std::runtime_error("readlink /proc/self/exe failed");
        }
        // readlink 不会自动追加 '\0'
        if (static_cast<size_t>(length) < buffer.size()) {
            return std::filesystem::path(
                std::string(buffer.data(), static_cast<size_t>(length))
            );
        }

        // 缓冲区不够，扩容重试
        buffer.resize(buffer.size() * 2);
    }

#else
#error "get_module_path is not implemented for this platform"
#endif
}

std::filesystem::path get_module_dir() {
    return get_module_path().parent_path();
}
}

//region 其他
namespace Z {
void print_obj_memory(const void *obj, size_t len) {
    auto bytes = static_cast<const unsigned char *>(obj);
    int l = static_cast<int>(len);
    int w = 1;
    while ((l /= 10) > 0) {
        w++;
    }
    for (size_t i = 0; i < len; i += 8) {
        for (size_t j = 0; j < 8; ++j) {
            auto sum = i + j;
            if (sum == len) break;
            std::cout << '[' << std::setfill('0') << std::setw(w) << sum << ']'
                    << std::setw(3) << static_cast<int>(bytes[sum]) << ' ';
        }
        std::cout << '\n';
    }
    std::cout << std::flush;
}

bool Random::bInitialized = false;
void *Random::m_gen = nullptr;
uint32_t Random::m_seed = 0;
int Random::Get(int min, int max) {
    InitOnce();
    std::uniform_int_distribution dist(min, max);
    return dist(*static_cast<std::mt19937 *>(m_gen));
}
float Random::Get(float min, float max) {
    InitOnce();
    std::uniform_real_distribution dist(min, max);
    return dist(*static_cast<std::mt19937 *>(m_gen));
}
double Random::Get(double min, double max) {
    InitOnce();
    std::uniform_real_distribution dist(min, max);
    return dist(*static_cast<std::mt19937 *>(m_gen));
}
void Random::GenerateSequence() {
    if (!bInitialized) {
        InitOnce();
    } else {
        GenSequence();
    }
}
void Random::GenerateSequence(uint32_t seed) {
    if (!bInitialized) {
        InitOnce();
    }
    GenSequence(seed);
}
void Random::InitOnce() {
    std::call_once(initflag, []() {
        GenSequence();
        std::atexit(CleanUp);
        bInitialized = true;
    });
}
void Random::GenSequence() {
    if (m_gen) {
        auto *_gen = static_cast<std::mt19937 *>(m_gen);
        delete _gen;
    }
    std::random_device rd{};
    m_seed = rd();
    m_gen = new std::mt19937(m_seed);
}
void Random::GenSequence(uint32_t seed) {
    m_seed = seed;
    if (m_gen) {
        auto *_gen = static_cast<std::mt19937 *>(m_gen);
        delete _gen;
    }
    m_gen = new std::mt19937(seed);
}
void Random::CleanUp() {
    if (m_gen) {
        auto *_gen = static_cast<std::mt19937 *>(m_gen);
        delete _gen;
        m_gen = nullptr;
        bInitialized = false;
    }
}

}














//
