// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "color_var_resolver.h"
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstdio>

namespace dragonx {
namespace ui {
namespace schema {

// ============================================================================
// Palette management
// ============================================================================

void ColorVarResolver::setPalette(const std::unordered_map<std::string, ImU32>& palette) {
    palette_ = palette;
}

// ============================================================================
// Public API
// ============================================================================

ImU32 ColorVarResolver::resolve(const std::string& value, ImU32 fallback) const {
    if (value.empty()) {
        return fallback;
    }

    // "transparent"
    if (value == "transparent") {
        return IM_COL32(0, 0, 0, 0);
    }

    // var(--name) → palette lookup
    if (value.size() > 6 && value.substr(0, 4) == "var(") {
        // Extract variable name: var(--primary) → --primary
        size_t close = value.find(')');
        if (close == std::string::npos) {
            return fallback;
        }
        std::string varName = value.substr(4, close - 4);

        // Trim whitespace
        while (!varName.empty() && varName.front() == ' ') varName.erase(varName.begin());
        while (!varName.empty() && varName.back() == ' ') varName.pop_back();

        auto it = palette_.find(varName);
        if (it != palette_.end()) {
            return it->second;
        }
        return fallback;
    }

    // #hex
    if (value[0] == '#') {
        ImU32 out = 0;
        if (parseHex(value, out)) {
            return out;
        }
        return fallback;
    }

    // rgba(r,g,b,a)
    if (value.size() > 5 && value.substr(0, 5) == "rgba(") {
        ImU32 out = 0;
        if (parseRgba(value, out)) {
            return out;
        }
        return fallback;
    }

    // 0xRRGGBB / 0xRRGGBBAA (legacy format support)
    if (value.size() > 2 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) {
        ImU32 out = 0;
        if (parseHex(value, out)) {
            return out;
        }
        return fallback;
    }

    return fallback;
}

bool ColorVarResolver::hasValue(const std::string& value) {
    return !value.empty();
}

// ============================================================================
// Static parsers
// ============================================================================

bool ColorVarResolver::parseHex(const std::string& hexStr, ImU32& out) {
    if (hexStr.empty()) return false;

    std::string hex = hexStr;

    // Remove leading # or 0x
    if (hex[0] == '#') {
        hex = hex.substr(1);
    } else if (hex.size() > 2 && hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) {
        hex = hex.substr(2);
    }

    // Validate length
    if (hex.size() != 6 && hex.size() != 8) {
        return false;
    }

    // Validate hex chars
    for (char c : hex) {
        if (!std::isxdigit(static_cast<unsigned char>(c))) {
            return false;
        }
    }

    unsigned long value = std::strtoul(hex.c_str(), nullptr, 16);

    if (hex.size() == 6) {
        // #RRGGBB → IM_COL32(R, G, B, 255)
        uint8_t r = (value >> 16) & 0xFF;
        uint8_t g = (value >> 8) & 0xFF;
        uint8_t b = value & 0xFF;
        out = IM_COL32(r, g, b, 255);
    } else {
        // #RRGGBBAA → IM_COL32(R, G, B, A)
        uint8_t r = (value >> 24) & 0xFF;
        uint8_t g = (value >> 16) & 0xFF;
        uint8_t b = (value >> 8) & 0xFF;
        uint8_t a = value & 0xFF;
        out = IM_COL32(r, g, b, a);
    }

    return true;
}

bool ColorVarResolver::parseRgba(const std::string& rgba, ImU32& out) {
    // Expected: "rgba(183,28,28,0.5)" or "rgba(183, 28, 28, 0.5)"
    if (rgba.size() < 11) return false;  // minimum: "rgba(0,0,0,0)"

    // Find opening paren
    size_t open = rgba.find('(');
    size_t close = rgba.rfind(')');
    if (open == std::string::npos || close == std::string::npos || close <= open) {
        return false;
    }

    std::string inner = rgba.substr(open + 1, close - open - 1);

    // Parse 4 comma-separated values
    float values[4] = {0, 0, 0, 0};
    int count = 0;
    const char* p = inner.c_str();

    for (int i = 0; i < 4 && *p; ++i) {
        // Skip whitespace
        while (*p == ' ' || *p == '\t') ++p;

        char* end = nullptr;
        values[i] = std::strtof(p, &end);
        if (end == p) break;  // failed to parse
        count++;

        p = end;
        // Skip whitespace and comma
        while (*p == ' ' || *p == '\t') ++p;
        if (*p == ',') ++p;
    }

    if (count != 4) return false;

    // r,g,b are 0-255 integers, a is 0.0-1.0 float
    uint8_t r = static_cast<uint8_t>(values[0] < 0 ? 0 : (values[0] > 255 ? 255 : values[0]));
    uint8_t g = static_cast<uint8_t>(values[1] < 0 ? 0 : (values[1] > 255 ? 255 : values[1]));
    uint8_t b = static_cast<uint8_t>(values[2] < 0 ? 0 : (values[2] > 255 ? 255 : values[2]));
    uint8_t a = static_cast<uint8_t>(values[3] < 0 ? 0 : (values[3] > 1.0f ? 255 : values[3] * 255.0f));

    out = IM_COL32(r, g, b, a);
    return true;
}

} // namespace schema
} // namespace ui
} // namespace dragonx
