#include "gfx/gl_loader.h"
#include "gfx/shader_canvas.h"
#include "imgui.h"
#include <functional>

namespace fn::gfx {

void canvas_init(ShaderCanvas& c) {
    if (c.initialized) return;
    gl_loader_init();
    fb_init(c.fb);
    quad_init(c.quad);
    c.initialized = true;
}

void canvas_set_program(ShaderCanvas& c, unsigned int program) {
    if (c.program) delete_program(c.program);
    c.program = program;
}

void canvas_render(ShaderCanvas& c, float time_seconds,
                   const std::function<void(unsigned int program)>& uniforms_fn) {
    ImVec2 avail = ImGui::GetContentRegionAvail();
    int w = static_cast<int>(avail.x);
    int h = static_cast<int>(avail.y);
    if (w < 1) w = 1;
    if (h < 1) h = 1;

    fb_resize(c.fb, w, h);

    // Save GL state
    GLint prev_fbo = 0;
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
    GLint prev_vp[4];
    glGetIntegerv(GL_VIEWPORT, prev_vp);

    // Render to FBO
    glBindFramebuffer(GL_FRAMEBUFFER, c.fb.fbo);
    glViewport(0, 0, w, h);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    if (c.program) {
        glUseProgram(c.program);

        GLint loc_res = glGetUniformLocation(c.program, "u_resolution");
        GLint loc_time = glGetUniformLocation(c.program, "u_time");
        GLint loc_mouse = glGetUniformLocation(c.program, "u_mouse");

        if (loc_res >= 0) glUniform2f(loc_res, static_cast<float>(w), static_cast<float>(h));
        if (loc_time >= 0) glUniform1f(loc_time, time_seconds);
        if (loc_mouse >= 0) {
            ImVec2 mouse = ImGui::GetMousePos();
            ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
            // cursor pos is AFTER content region starts
            float mx = mouse.x - canvas_pos.x;
            float my = static_cast<float>(h) - (mouse.y - canvas_pos.y);
            glUniform2f(loc_mouse, mx, my);
        }

        if (uniforms_fn) uniforms_fn(c.program);

        quad_draw(c.quad);
        glUseProgram(0);
    }

    // Restore GL state
    glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo));
    glViewport(prev_vp[0], prev_vp[1], prev_vp[2], prev_vp[3]);

    // Draw texture in ImGui panel (flip V: OpenGL origin is bottom-left)
    ImGui::Image(
        (ImTextureID)(intptr_t)c.fb.tex,
        avail,
        ImVec2(0, 1), ImVec2(1, 0)
    );
}

void canvas_destroy(ShaderCanvas& c) {
    if (!c.initialized) return;
    if (c.program) { delete_program(c.program); c.program = 0; }
    quad_destroy(c.quad);
    fb_destroy(c.fb);
    c.initialized = false;
}

} // namespace fn::gfx
