// Demos del dominio gfx — primitivos OpenGL/shader que viven en
// cpp/functions/gfx/. La pieza distintiva de shaders_lab es el
// shader_canvas: framebuffer + fullscreen quad + programa GL animado por
// time/resolution/mouse.

#include "demos.h"
#include "demo.h"

#include "gfx/shader_canvas.h"
#include "gfx/gl_shader.h"
#include "gfx/gl_loader.h"

#include <imgui.h>
#include <chrono>

namespace gallery {

namespace {

// Fragment shader sintetico — gradiente animado con celdas. Usa los uniforms
// estandar que compile_fragment inyecta: u_resolution, u_time, u_mouse.
const char* kShaderSrc = R"(
void mainImage() {
    vec2 uv = gl_FragCoord.xy / u_resolution;
    vec2 cell = uv * 8.0;
    vec2 ipos = floor(cell);
    vec2 fpos = fract(cell) - 0.5;

    float t = u_time * 0.6;
    float wave = sin(ipos.x * 0.7 + ipos.y * 0.5 + t);
    float dist = length(fpos);

    vec3 a = vec3(0.30, 0.43, 0.96); // indigo
    vec3 b = vec3(0.95, 0.45, 0.85); // pink
    vec3 col = mix(a, b, 0.5 + 0.5 * wave);

    // Mouse focus: oscurecemos celdas lejanas al cursor.
    vec2 m = u_mouse / u_resolution;
    float fm = 1.0 - smoothstep(0.0, 0.6, length(uv - m));
    col *= 0.6 + 0.4 * fm;

    // Disco interior por celda con borde suave.
    col *= smoothstep(0.5, 0.45, dist);

    fragColor = vec4(col, 1.0);
}

void main() {
    mainImage();
}
)";

struct CanvasState {
    fn::gfx::ShaderCanvas canvas;
    bool   compiled        = false;
    bool   compile_failed  = false;
    std::string err_msg;
    std::chrono::steady_clock::time_point t0;
};

CanvasState& state() {
    static CanvasState s;
    return s;
}

} // namespace

void demo_shader_canvas() {
    demo_header("shader_canvas", "v1.0.0",
        "Framebuffer + fullscreen quad + shader GLSL animado. La misma pieza "
        "que usa shaders_lab para el preview en vivo. Uniforms u_time / u_resolution / u_mouse "
        "los inyecta gl_shader::compile_fragment automaticamente.");

    auto& s = state();

    // Compilacion lazy (en el primer frame ya hay contexto GL valido).
    if (!s.compiled && !s.compile_failed) {
        fn::gfx::gl_loader_init();
        fn::gfx::canvas_init(s.canvas);

        auto cr = fn::gfx::compile_fragment(kShaderSrc);
        if (!cr.ok) {
            s.compile_failed = true;
            s.err_msg = cr.err_msg;
        } else {
            fn::gfx::canvas_set_program(s.canvas, cr.program);
            s.t0 = std::chrono::steady_clock::now();
            s.compiled = true;
        }
    }

    if (s.compile_failed) {
        ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1),
            "Compilacion del fragment shader fallo:\n%s",
            s.err_msg.c_str());
        return;
    }

    section("Live preview");

    // Render del shader en un panel ~480x300 px. canvas_render hace resize
    // automatico segun GetContentRegionAvail si lo dejas crecer.
    ImGui::BeginChild("##shader_preview", ImVec2(480, 300),
                      ImGuiChildFlags_Borders);
    const float dt = std::chrono::duration<float>(
        std::chrono::steady_clock::now() - s.t0).count();
    fn::gfx::canvas_render(s.canvas, dt);
    ImGui::EndChild();

    code_block(
        "#include \"gfx/shader_canvas.h\"\n"
        "#include \"gfx/gl_shader.h\"\n\n"
        "static fn::gfx::ShaderCanvas canvas;\n"
        "// Setup (una vez):\n"
        "fn::gfx::canvas_init(canvas);\n"
        "auto cr = fn::gfx::compile_fragment(user_glsl);\n"
        "if (cr.ok) fn::gfx::canvas_set_program(canvas, cr.program);\n\n"
        "// Cada frame, dentro de un Begin/End:\n"
        "fn::gfx::canvas_render(canvas, time_seconds);"
    );
}

} // namespace gallery
