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

#include "element_styles.h"
#include <toml++/toml.hpp>

namespace dragonx {
namespace ui {
namespace schema {

// ============================================================================
// TOML helper macros — read optional fields, leave sentinel if absent
// ============================================================================

#define READ_FLOAT(t, field, out)       do { auto _v = (t)[field].value<double>(); if (_v) (out) = static_cast<float>(*_v); } while(0)
#define READ_INT(t, field, out)         do { auto _v = (t)[field].value<int64_t>(); if (_v) (out) = static_cast<int>(*_v); } while(0)
#define READ_STRING(t, field, out)      do { auto _v = (t)[field].value<std::string>(); if (_v) (out) = *_v; } while(0)
#define READ_PADDING(t, field, out)     do { \
    if ((t).contains(field)) { \
        if (auto* _arr = (t)[field].as_array(); _arr && _arr->size() >= 2) { \
            auto _v0 = (*_arr)[0].value<double>(); \
            auto _v1 = (*_arr)[1].value<double>(); \
            if (_v0) (out)[0] = static_cast<float>(*_v0); \
            if (_v1) (out)[1] = static_cast<float>(*_v1); \
        } else { \
            auto _v = (t)[field].value<double>(); \
            if (_v) (out)[0] = (out)[1] = static_cast<float>(*_v); \
        } \
    } \
} while(0)

// ============================================================================
// Parse functions — cast void* back to toml::table
// ============================================================================

namespace detail {

void parseElementColors(const void* dataObj, ElementColors& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_STRING(t, "color", out.color);
    READ_STRING(t, "background", out.background);
    READ_STRING(t, "background-hover", out.backgroundHover);
    READ_STRING(t, "background-active", out.backgroundActive);
    READ_STRING(t, "border-color", out.borderColor);
}

void parseButtonStyle(const void* dataObj, ButtonStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_FLOAT(t, "height", out.height);
    READ_STRING(t, "font", out.font);
    READ_PADDING(t, "padding", out.padding);
    READ_FLOAT(t, "opacity", out.opacity);
    READ_FLOAT(t, "border-radius", out.borderRadius);
    READ_FLOAT(t, "border-width", out.borderWidth);
    READ_FLOAT(t, "min-width", out.minWidth);
    READ_STRING(t, "align", out.align);
    READ_FLOAT(t, "gap", out.gap);
    parseElementColors(dataObj, out.colors);
}

void parseInputStyle(const void* dataObj, InputStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_FLOAT(t, "height", out.height);
    READ_INT(t, "lines", out.lines);
    READ_STRING(t, "font", out.font);
    READ_PADDING(t, "padding", out.padding);
    READ_FLOAT(t, "border-radius", out.borderRadius);
    READ_FLOAT(t, "border-width", out.borderWidth);
    READ_STRING(t, "border-color-focus", out.borderColorFocus);
    READ_STRING(t, "placeholder-color", out.placeholderColor);
    READ_FLOAT(t, "width-ratio", out.widthRatio);
    READ_FLOAT(t, "max-width", out.maxWidth);
    parseElementColors(dataObj, out.colors);
}

void parseLabelStyle(const void* dataObj, LabelStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_STRING(t, "font", out.font);
    READ_STRING(t, "color", out.color);
    READ_FLOAT(t, "opacity", out.opacity);
    READ_STRING(t, "align", out.align);
    READ_INT(t, "truncate", out.truncate);
    READ_FLOAT(t, "position", out.position);
}

void parseTableStyle(const void* dataObj, TableStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "min-height", out.minHeight);
    READ_FLOAT(t, "height-ratio", out.heightRatio);
    READ_FLOAT(t, "bottom-reserve", out.bottomReserve);
    READ_FLOAT(t, "row-height", out.rowHeight);
    READ_STRING(t, "header-font", out.headerFont);
    READ_STRING(t, "cell-font", out.cellFont);
    READ_STRING(t, "border-color", out.borderColor);
    READ_STRING(t, "stripe-color", out.stripeColor);

    if (auto* cols = t["columns"].as_table()) {
        for (auto&& [key, val] : *cols) {
            auto* colTable = val.as_table();
            if (!colTable) continue;
            ColumnStyle col;
            READ_FLOAT(*colTable, "width", col.width);
            READ_STRING(*colTable, "font", col.font);
            READ_STRING(*colTable, "align", col.align);
            READ_INT(*colTable, "truncate", col.truncate);
            out.columns[std::string(key.str())] = col;
        }
    }
}

void parseCheckboxStyle(const void* dataObj, CheckboxStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_STRING(t, "font", out.font);
    READ_STRING(t, "color", out.color);
    READ_STRING(t, "check-color", out.checkColor);
    READ_STRING(t, "background", out.background);
}

void parseComboStyle(const void* dataObj, ComboStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_STRING(t, "font", out.font);
    READ_STRING(t, "color", out.color);
    READ_STRING(t, "background", out.background);
    READ_FLOAT(t, "border-radius", out.borderRadius);
    READ_INT(t, "truncate", out.truncate);
}

void parseSliderStyle(const void* dataObj, SliderStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_STRING(t, "track-color", out.trackColor);
    READ_STRING(t, "fill-color", out.fillColor);
    READ_STRING(t, "thumb-color", out.thumbColor);
    READ_FLOAT(t, "thumb-radius", out.thumbRadius);
}

void parseWindowStyle(const void* dataObj, WindowStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_FLOAT(t, "height", out.height);
    READ_PADDING(t, "padding", out.padding);
    READ_FLOAT(t, "border-radius", out.borderRadius);
    READ_FLOAT(t, "border-width", out.borderWidth);
    READ_STRING(t, "background", out.background);
    READ_STRING(t, "border-color", out.borderColor);
    READ_STRING(t, "title-font", out.titleFont);
}

void parseSeparatorStyle(const void* dataObj, SeparatorStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_STRING(t, "color", out.color);
    READ_FLOAT(t, "thickness", out.thickness);
    READ_PADDING(t, "margin", out.margin);
}

void parseDrawElementStyle(const void* dataObj, DrawElementStyle& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_STRING(t, "color", out.color);
    READ_STRING(t, "background", out.background);
    READ_FLOAT(t, "thickness", out.thickness);
    READ_FLOAT(t, "radius", out.radius);
    READ_FLOAT(t, "opacity", out.opacity);
    READ_FLOAT(t, "size", out.size);
    READ_FLOAT(t, "height", out.height);

    // Collect any extra color/float properties not in the standard set
    static const char* knownKeys[] = {
        "color", "background", "thickness", "radius", "opacity", "size", "height",
        nullptr
    };
    for (auto&& [key, val] : t) {
        // Skip known keys
        bool known = false;
        for (const char** k = knownKeys; *k; ++k) {
            if (key.str() == *k) { known = true; break; }
        }
        if (known) continue;

        if (val.is_string()) {
            out.extraColors[std::string(key.str())] = *val.value<std::string>();
        } else if (val.is_integer() || val.is_floating_point()) {
            out.extraFloats[std::string(key.str())] = static_cast<float>(val.value<double>().value_or(0.0));
        }
    }
}

void parseBreakpointConfig(const void* dataObj, BreakpointConfig& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);

    if (auto* c = t["compact"].as_table()) {
        READ_FLOAT(*c, "max-width", out.compact.maxWidth);
        READ_FLOAT(*c, "max-height", out.compact.maxHeight);
        READ_FLOAT(*c, "min-width", out.compact.minWidth);
        READ_FLOAT(*c, "min-height", out.compact.minHeight);
    }
    if (auto* e = t["expanded"].as_table()) {
        READ_FLOAT(*e, "max-width", out.expanded.maxWidth);
        READ_FLOAT(*e, "max-height", out.expanded.maxHeight);
        READ_FLOAT(*e, "min-width", out.expanded.minWidth);
        READ_FLOAT(*e, "min-height", out.expanded.minHeight);
    }
}

void parseResponsiveButtonOverride(const void* dataObj, ResponsiveButtonOverride& out) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);
    READ_FLOAT(t, "width", out.width);
    READ_FLOAT(t, "height", out.height);
    READ_STRING(t, "font", out.font);
    READ_PADDING(t, "padding", out.padding);
}

} // namespace detail

// ============================================================================
// Merge helpers
// ============================================================================

void mergeButton(ButtonStyle& base, const ButtonStyle& overlay) {
    if (overlay.width > 0)         base.width = overlay.width;
    if (overlay.height > 0)        base.height = overlay.height;
    if (!overlay.font.empty())     base.font = overlay.font;
    if (overlay.padding[0] > 0)    { base.padding[0] = overlay.padding[0]; base.padding[1] = overlay.padding[1]; }
    if (overlay.opacity >= 0)      base.opacity = overlay.opacity;
    if (overlay.borderRadius >= 0) base.borderRadius = overlay.borderRadius;
    if (overlay.borderWidth >= 0)  base.borderWidth = overlay.borderWidth;
    if (overlay.minWidth >= 0)     base.minWidth = overlay.minWidth;
    if (!overlay.align.empty())    base.align = overlay.align;
    if (overlay.gap > 0)           base.gap = overlay.gap;

    // Colors: non-empty string overrides
    if (!overlay.colors.color.empty())            base.colors.color = overlay.colors.color;
    if (!overlay.colors.background.empty())        base.colors.background = overlay.colors.background;
    if (!overlay.colors.backgroundHover.empty())   base.colors.backgroundHover = overlay.colors.backgroundHover;
    if (!overlay.colors.backgroundActive.empty())  base.colors.backgroundActive = overlay.colors.backgroundActive;
    if (!overlay.colors.borderColor.empty())        base.colors.borderColor = overlay.colors.borderColor;
}

void mergeInput(InputStyle& base, const InputStyle& overlay) {
    if (overlay.width != 0)        base.width = overlay.width;
    if (overlay.height > 0)        base.height = overlay.height;
    if (overlay.lines > 0)         base.lines = overlay.lines;
    if (!overlay.font.empty())     base.font = overlay.font;
    if (overlay.padding[0] > 0)    { base.padding[0] = overlay.padding[0]; base.padding[1] = overlay.padding[1]; }
    if (overlay.borderRadius >= 0) base.borderRadius = overlay.borderRadius;
    if (overlay.borderWidth >= 0)  base.borderWidth = overlay.borderWidth;
    if (!overlay.borderColorFocus.empty())  base.borderColorFocus = overlay.borderColorFocus;
    if (!overlay.placeholderColor.empty())  base.placeholderColor = overlay.placeholderColor;
    if (overlay.widthRatio >= 0)   base.widthRatio = overlay.widthRatio;
    if (overlay.maxWidth >= 0)     base.maxWidth = overlay.maxWidth;

    if (!overlay.colors.color.empty())            base.colors.color = overlay.colors.color;
    if (!overlay.colors.background.empty())        base.colors.background = overlay.colors.background;
    if (!overlay.colors.borderColor.empty())        base.colors.borderColor = overlay.colors.borderColor;
}

void mergeLabel(LabelStyle& base, const LabelStyle& overlay) {
    if (!overlay.font.empty())     base.font = overlay.font;
    if (!overlay.color.empty())    base.color = overlay.color;
    if (overlay.opacity >= 0)      base.opacity = overlay.opacity;
    if (!overlay.align.empty())    base.align = overlay.align;
    if (overlay.truncate > 0)      base.truncate = overlay.truncate;
    if (overlay.position >= 0)     base.position = overlay.position;
}

void mergeWindow(WindowStyle& base, const WindowStyle& overlay) {
    if (overlay.width > 0)          base.width = overlay.width;
    if (overlay.height > 0)         base.height = overlay.height;
    if (overlay.padding[0] > 0)     { base.padding[0] = overlay.padding[0]; base.padding[1] = overlay.padding[1]; }
    if (overlay.borderRadius >= 0)  base.borderRadius = overlay.borderRadius;
    if (overlay.borderWidth >= 0)   base.borderWidth = overlay.borderWidth;
    if (!overlay.background.empty()) base.background = overlay.background;
    if (!overlay.borderColor.empty()) base.borderColor = overlay.borderColor;
    if (!overlay.titleFont.empty())  base.titleFont = overlay.titleFont;
}

void applyResponsiveButton(ButtonStyle& base, const ResponsiveButtonOverride& ovr) {
    if (ovr.width > 0)          base.width = ovr.width;
    if (ovr.height > 0)         base.height = ovr.height;
    if (!ovr.font.empty())      base.font = ovr.font;
    if (ovr.padding[0] > 0)     { base.padding[0] = ovr.padding[0]; base.padding[1] = ovr.padding[1]; }
}

#undef READ_FLOAT
#undef READ_INT
#undef READ_STRING
#undef READ_PADDING

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