#include "gfx/uniform_panel.h"
#include "gfx/gl_loader.h"
#include "imgui.h"

namespace fn::gfx {

void uniforms_sync(UniformStore& store, const std::vector<UniformDescriptor>& descs) {
    // Remove keys no longer present
    for (auto it = store.values.begin(); it != store.values.end(); ) {
        bool found = false;
        for (auto& d : descs) if (d.name == it->first) { found = true; break; }
        it = found ? std::next(it) : store.values.erase(it);
    }
    // Add new entries with defaults
    for (auto& d : descs) {
        if (store.values.count(d.name)) continue;
        std::array<float, 4> arr = {
            d.defaults[0], d.defaults[1], d.defaults[2], d.defaults[3]
        };
        store.values[d.name] = arr;
    }
}

void uniforms_panel(UniformStore& store, const std::vector<UniformDescriptor>& descs) {
    if (descs.empty()) {
        ImGui::TextDisabled("Declare uniforms with @slider ... annotations.");
        return;
    }

    for (auto& d : descs) {
        auto it = store.values.find(d.name);
        if (it == store.values.end()) continue;
        float* v = it->second.data();

        switch (d.widget) {
            case WidgetKind::Slider: {
                if (d.glsl_type == GLSLType::Int) {
                    int tmp = static_cast<int>(v[0]);
                    if (ImGui::SliderInt(d.name.c_str(), &tmp,
                                        static_cast<int>(d.min[0]),
                                        static_cast<int>(d.max[0]))) {
                        v[0] = static_cast<float>(tmp);
                    }
                } else {
                    ImGuiSliderFlags flags = d.log_scale ? ImGuiSliderFlags_Logarithmic : 0;
                    ImGui::SliderFloat(d.name.c_str(), &v[0], d.min[0], d.max[0], "%.3f", flags);
                }
                break;
            }
            case WidgetKind::Color: {
                if (d.glsl_type == GLSLType::Vec4)
                    ImGui::ColorEdit4(d.name.c_str(), v);
                else
                    ImGui::ColorEdit3(d.name.c_str(), v);
                break;
            }
            case WidgetKind::Toggle: {
                bool tmp = v[0] != 0.0f;
                if (ImGui::Checkbox(d.name.c_str(), &tmp))
                    v[0] = tmp ? 1.0f : 0.0f;
                break;
            }
            case WidgetKind::XY: {
                ImGui::SliderFloat2(d.name.c_str(), v, d.min[0], d.max[0]);
                break;
            }
        }
    }
}

void uniforms_apply(const UniformStore& store, const std::vector<UniformDescriptor>& descs, unsigned int program) {
    for (auto& d : descs) {
        auto it = store.values.find(d.name);
        if (it == store.values.end()) continue;
        const float* v = it->second.data();

        GLint loc = glGetUniformLocation(program, d.name.c_str());
        if (loc < 0) continue;

        switch (d.glsl_type) {
            case GLSLType::Float: glUniform1f(loc, v[0]);                       break;
            case GLSLType::Int:   glUniform1i(loc, static_cast<GLint>(v[0]));   break;
            case GLSLType::Bool:  glUniform1i(loc, v[0] != 0.0f ? 1 : 0);      break;
            case GLSLType::Vec2:  glUniform2f(loc, v[0], v[1]);                 break;
            case GLSLType::Vec3:  glUniform3f(loc, v[0], v[1], v[2]);           break;
            case GLSLType::Vec4:  glUniform4f(loc, v[0], v[1], v[2], v[3]);     break;
        }
    }
}

} // namespace fn::gfx
