#include "gfx/uniform_parser.h"

#include <regex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace fn::gfx {

static const std::unordered_set<std::string> k_reserved = {
    "u_resolution", "u_time", "u_mouse"
};

// Parse a comma-separated list of up to 4 floats into out[]. Returns count parsed.
static int parse_floats(const std::string& s, float out[4]) {
    int n = 0;
    std::istringstream ss(s);
    std::string tok;
    while (n < 4 && std::getline(ss, tok, ',')) {
        try { out[n++] = std::stof(tok); } catch (...) {}
    }
    return n;
}

// Parse `key=value` pairs from annotation tail.
static std::unordered_map<std::string, std::string> parse_props(const std::string& tail) {
    std::unordered_map<std::string, std::string> props;
    std::regex kv_re(R"((\w+)=([^\s]+))");
    auto begin = std::sregex_iterator(tail.begin(), tail.end(), kv_re);
    auto end   = std::sregex_iterator();
    for (auto it = begin; it != end; ++it) {
        props[(*it)[1].str()] = (*it)[2].str();
    }
    return props;
}

static void apply_defaults_for_type(UniformDescriptor& d) {
    switch (d.glsl_type) {
        case GLSLType::Float:
            d.widget = WidgetKind::Slider;
            d.min[0] = 0.0f; d.max[0] = 1.0f;
            d.defaults[0] = 0.0f;
            break;
        case GLSLType::Int:
            d.widget = WidgetKind::Slider;
            d.min[0] = 0.0f; d.max[0] = 10.0f;
            d.step = 1.0f;
            d.defaults[0] = 0.0f;
            break;
        case GLSLType::Bool:
            d.widget = WidgetKind::Toggle;
            d.default_bool = false;
            d.defaults[0] = 0.0f;
            break;
        case GLSLType::Vec2:
            d.widget = WidgetKind::XY;
            d.min[0] = d.min[1] = 0.0f;
            d.max[0] = d.max[1] = 1.0f;
            d.defaults[0] = d.defaults[1] = 0.5f;
            break;
        case GLSLType::Vec3:
            d.widget = WidgetKind::Color;
            d.defaults[0] = d.defaults[1] = d.defaults[2] = 1.0f;
            break;
        case GLSLType::Vec4:
            d.widget = WidgetKind::Color;
            d.defaults[0] = d.defaults[1] = d.defaults[2] = d.defaults[3] = 1.0f;
            break;
    }
}

static void apply_props(UniformDescriptor& d, const std::unordered_map<std::string, std::string>& props) {
    auto get = [&](const char* k) -> const std::string* {
        auto it = props.find(k);
        return it != props.end() ? &it->second : nullptr;
    };

    if (auto* v = get("min")) {
        float tmp[4] = {d.min[0], d.min[1], d.min[2], d.min[3]};
        int n = parse_floats(*v, tmp);
        for (int i = 0; i < 4; i++) d.min[i] = (i < n) ? tmp[i] : tmp[n > 0 ? n-1 : 0];
    }
    if (auto* v = get("max")) {
        float tmp[4] = {d.max[0], d.max[1], d.max[2], d.max[3]};
        int n = parse_floats(*v, tmp);
        for (int i = 0; i < 4; i++) d.max[i] = (i < n) ? tmp[i] : tmp[n > 0 ? n-1 : 0];
    }
    if (auto* v = get("default")) {
        if (d.glsl_type == GLSLType::Bool) {
            d.default_bool = (*v == "true" || *v == "1");
            d.defaults[0] = d.default_bool ? 1.0f : 0.0f;
        } else {
            float tmp[4] = {};
            int n = parse_floats(*v, tmp);
            for (int i = 0; i < 4; i++) d.defaults[i] = (i < n) ? tmp[i] : 0.0f;
        }
    }
    if (auto* v = get("step")) {
        try { d.step = std::stof(*v); } catch (...) {}
    }
    if (auto* v = get("log")) {
        d.log_scale = (*v == "true");
    }
}

std::vector<UniformDescriptor> parse_uniforms(const std::string& glsl_source) {
    // Match: uniform <type> <name>; optionally followed by // @<widget> <props>
    static const std::regex line_re(
        R"(^\s*uniform\s+(\w+)\s+(\w+)\s*;(?:\s*//\s*@(\w+)(.*))?\s*$)"
    );

    std::vector<UniformDescriptor> result;
    std::istringstream stream(glsl_source);
    std::string line;

    while (std::getline(stream, line)) {
        std::smatch m;
        if (!std::regex_match(line, m, line_re)) continue;

        std::string type_str = m[1].str();
        std::string name_str = m[2].str();
        bool has_annotation  = m[3].matched;
        std::string widget_str = has_annotation ? m[3].str() : "";
        std::string props_str  = has_annotation ? m[4].str() : "";

        // Skip reserved and unsupported
        if (k_reserved.count(name_str)) continue;
        if (type_str == "sampler2D") continue;

        UniformDescriptor d;
        d.name = name_str;

        // Map GLSL type
        if      (type_str == "float") d.glsl_type = GLSLType::Float;
        else if (type_str == "int")   d.glsl_type = GLSLType::Int;
        else if (type_str == "bool")  d.glsl_type = GLSLType::Bool;
        else if (type_str == "vec2")  d.glsl_type = GLSLType::Vec2;
        else if (type_str == "vec3")  d.glsl_type = GLSLType::Vec3;
        else if (type_str == "vec4")  d.glsl_type = GLSLType::Vec4;
        else continue; // unknown type — skip

        // Set defaults for type first
        apply_defaults_for_type(d);

        if (has_annotation) {
            // Override widget kind
            if      (widget_str == "slider") {
                d.widget = (d.glsl_type == GLSLType::Vec2) ? WidgetKind::XY : WidgetKind::Slider;
            }
            else if (widget_str == "color")  d.widget = WidgetKind::Color;
            else if (widget_str == "toggle") d.widget = WidgetKind::Toggle;
            else if (widget_str == "xy")     d.widget = WidgetKind::XY;

            auto props = parse_props(props_str);
            apply_props(d, props);
        }

        result.push_back(d);
    }

    return result;
}

} // namespace fn::gfx

// ──────────────────────────────────────────────
// Inline tests — compile with -DUNIFORM_PARSER_TEST
// ──────────────────────────────────────────────
#ifdef UNIFORM_PARSER_TEST
#include <cassert>
#include <cstdio>

using namespace fn::gfx;

int main() {
    // Test 1: sin anotación float → Slider 0..1
    {
        auto v = parse_uniforms("uniform float u_x;");
        assert(v.size() == 1);
        assert(v[0].name == "u_x");
        assert(v[0].glsl_type == GLSLType::Float);
        assert(v[0].widget == WidgetKind::Slider);
        assert(v[0].min[0] == 0.0f);
        assert(v[0].max[0] == 1.0f);
        assert(v[0].defaults[0] == 0.0f);
        printf("PASS: sin anotacion float -> Slider 0..1\n");
    }

    // Test 2: @slider min=0 max=5 default=1 sobre float
    {
        auto v = parse_uniforms("uniform float u_speed; // @slider min=0 max=5 default=1");
        assert(v.size() == 1);
        assert(v[0].widget == WidgetKind::Slider);
        assert(v[0].min[0] == 0.0f);
        assert(v[0].max[0] == 5.0f);
        assert(v[0].defaults[0] == 1.0f);
        printf("PASS: @slider min=0 max=5 default=1 sobre float\n");
    }

    // Test 3: @color default=0.1,0.2,0.5 sobre vec3
    {
        auto v = parse_uniforms("uniform vec3 u_color; // @color default=0.1,0.2,0.5");
        assert(v.size() == 1);
        assert(v[0].glsl_type == GLSLType::Vec3);
        assert(v[0].widget == WidgetKind::Color);
        assert(v[0].defaults[0] == 0.1f);
        assert(v[0].defaults[1] == 0.2f);
        assert(v[0].defaults[2] == 0.5f);
        printf("PASS: @color default=0.1,0.2,0.5 sobre vec3\n");
    }

    // Test 4: @xy min=-1 max=1 default=0,0 sobre vec2
    {
        auto v = parse_uniforms("uniform vec2 u_pos; // @xy min=-1 max=1 default=0,0");
        assert(v.size() == 1);
        assert(v[0].glsl_type == GLSLType::Vec2);
        assert(v[0].widget == WidgetKind::XY);
        assert(v[0].min[0] == -1.0f);
        assert(v[0].max[0] == 1.0f);
        assert(v[0].defaults[0] == 0.0f);
        assert(v[0].defaults[1] == 0.0f);
        printf("PASS: @xy min=-1 max=1 default=0,0 sobre vec2\n");
    }

    // Test 5: uniform vec2 u_resolution ignorado (reservado)
    {
        auto v = parse_uniforms("uniform vec2 u_resolution;");
        assert(v.empty());
        printf("PASS: uniform vec2 u_resolution ignorado\n");
    }

    // Test 6: @toggle default=true sobre bool
    {
        auto v = parse_uniforms("uniform bool u_debug; // @toggle default=true");
        assert(v.size() == 1);
        assert(v[0].glsl_type == GLSLType::Bool);
        assert(v[0].widget == WidgetKind::Toggle);
        assert(v[0].default_bool == true);
        assert(v[0].defaults[0] == 1.0f);
        printf("PASS: @toggle default=true sobre bool\n");
    }

    printf("---\nAll 6 tests passed.\n");
    return 0;
}
#endif
