#pragma once

#include <array>
#include <cstdint>
#include <map>
#include <string>
#include <fstream>
#include <sstream>

// ==========================================================
// OffsetsStore - Lưu trữ offset đã quét ra file cấu hình
// ==========================================================
class OffsetsStore {
public:
    static constexpr std::array<const char*, 9> kForbiddenAbsoluteKeys = {
        "player_addr", "xyz_addr", "shield_addr", "mana_addr",
        "spirit_addr", "ingame_addr", "terrain_addr",
        "camera_addr", "fog_density_addr"
    };

    static bool IsForbiddenAbsoluteKey(const std::string& key) {
        for (const char* forbidden : kForbiddenAbsoluteKeys) {
            if (key == forbidden) {
                return true;
            }
        }
        return false;
    }

    bool Load(const std::string& path) {
        std::ifstream file(path);
        if (!file) return false;

        std::string line;
        while (std::getline(file, line)) {
            const auto hash = line.find('#');
            if (hash != std::string::npos) line.erase(hash);
            if (line.empty()) continue;
            if (line.front() == '[') continue;

            const auto eq = line.find('=');
            if (eq == std::string::npos) continue;

            auto key = Trim(line.substr(0, eq));
            auto val = Trim(line.substr(eq + 1));
            if (key.empty() || val.empty()) continue;

            if (IsForbiddenAbsoluteKey(key)) {
                ++m_rejectedOnLoad;
                continue;
            }

            uint64_t numeric = 0;
            if (TryParseValue(val, numeric)) {
                m_offsets[key] = numeric;
            } else {
                m_strings[key] = val;
            }
        }
        return true;
    }

    bool Save(const std::string& path) const {
        std::ofstream file(path);
        if (!file) return false;

        file << "# Poe2VisualTool offsets cache\n";
        for (const auto& [key, value] : m_offsets) {
            file << key << " = 0x" << std::hex << value << "\n";
        }
        for (const auto& [key, value] : m_strings) {
            file << key << " = " << value << "\n";
        }
        return true;
    }

    bool Set(const std::string& name, uint64_t value) {
        if (IsForbiddenAbsoluteKey(name)) {
            ++m_rejectedOnSet;
            m_offsets.erase(name);
            m_strings.erase(name);
            return false;
        }
        m_offsets[name] = value;
        m_strings.erase(name);
        return true;
    }

    bool SetString(const std::string& name, const std::string& value) {
        if (IsForbiddenAbsoluteKey(name)) {
            ++m_rejectedOnSet;
            return false;
        }
        m_strings[name] = value;
        m_offsets.erase(name);
        return true;
    }

    void Remove(const std::string& name) {
        m_offsets.erase(name);
        m_strings.erase(name);
    }

    uint64_t Get(const std::string& name, uint64_t defaultValue = 0) const {
        const auto it = m_offsets.find(name);
        return (it != m_offsets.end()) ? it->second : defaultValue;
    }

    bool Has(const std::string& name) const {
        return m_offsets.find(name) != m_offsets.end();
    }

    std::string GetString(const std::string& name, const std::string& defaultValue = "") const {
        const auto it = m_strings.find(name);
        return (it != m_strings.end()) ? it->second : defaultValue;
    }

    bool HasString(const std::string& name) const {
        return m_strings.find(name) != m_strings.end();
    }

    size_t NumericCount() const { return m_offsets.size(); }
    size_t StringCount() const { return m_strings.size(); }

    size_t RejectedOnLoadCount() const { return m_rejectedOnLoad; }
    size_t RejectedOnSetCount() const { return m_rejectedOnSet; }

private:
    static std::string Trim(const std::string& s) {
        const auto begin = s.find_first_not_of(" \t\r\n");
        if (begin == std::string::npos) return "";
        const auto end = s.find_last_not_of(" \t\r\n");
        return s.substr(begin, end - begin + 1);
    }

    static bool TryParseValue(const std::string& v, uint64_t& outValue) {
        if (v.empty()) return false;

        if (v == "true" || v == "TRUE") { outValue = 1; return true; }
        if (v == "false" || v == "FALSE") { outValue = 0; return true; }

        auto isHexDigit = [](char c) {
            return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
        };
        auto isDecDigit = [](char c) { return c >= '0' && c <= '9'; };

        try {
            if ((v.rfind("0x", 0) == 0 || v.rfind("0X", 0) == 0) && v.size() > 2) {
                for (size_t i = 2; i < v.size(); ++i) {
                    if (!isHexDigit(v[i])) return false;
                }
                outValue = std::stoull(v.substr(2), nullptr, 16);
                return true;
            }
            for (char c : v) {
                if (!isDecDigit(c)) return false;
            }
            outValue = std::stoull(v, nullptr, 10);
            return true;
        } catch (...) {
            return false;
        }
    }

    std::map<std::string, uint64_t> m_offsets;
    std::map<std::string, std::string> m_strings;
    size_t m_rejectedOnLoad = 0;
    size_t m_rejectedOnSet = 0;
};
