// Demos para los primitivos de animacion (issue 0031):
//   - tween_curves
//   - bezier_editor
//   - timeline

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

#include "core/tween_curves.h"
#include "core/bezier_editor.h"
#include "core/timeline.h"
#include "core/tokens.h"

#include <imgui.h>

#include <cstdio>
#include <cmath>

namespace gallery {

// ---------------------------------------------------------------------------
// demo_tween — dropdown + plot animado
// ---------------------------------------------------------------------------

void demo_tween() {
    using namespace fn_tokens;
    using fn::tween::Ease;

    demo_header("tween_curves", "v1.0.0",
        "Funciones de easing (Penner): linear, quad, cubic, expo, elastic, "
        "bounce con variantes in/out/inOut. Header-mostly: el compilador "
        "inlinea cada curva en el sitio de llamada.");

    section("Selector + plot");

    static int  ease_idx = (int)Ease::OutCubic;
    static float anim_t  = 0.0f;
    anim_t += ImGui::GetIO().DeltaTime * 0.5f;
    if (anim_t > 1.5f) anim_t = -0.25f; // hold un poco antes de reiniciar

    // Build labels
    const char* labels[fn::tween::ease_count];
    for (int i = 0; i < fn::tween::ease_count; i++) {
        labels[i] = fn::tween::name((Ease)i);
    }
    ImGui::SetNextItemWidth(220.0f);
    ImGui::Combo("##tween_ease", &ease_idx, labels, fn::tween::ease_count);

    Ease ease = (Ease)ease_idx;
    float t_clamped = anim_t;
    if (t_clamped < 0.0f) t_clamped = 0.0f;
    if (t_clamped > 1.0f) t_clamped = 1.0f;
    float v = fn::tween::apply(ease, t_clamped);

    ImGui::SameLine();
    ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
    ImGui::Text("  t=%.2f  f(t)=%.3f", t_clamped, v);
    ImGui::PopStyleColor();

    // Canvas plot
    ImVec2 canvas_min = ImGui::GetCursorScreenPos();
    ImVec2 canvas_size(360.0f, 220.0f);
    ImVec2 canvas_max = ImVec2(canvas_min.x + canvas_size.x, canvas_min.y + canvas_size.y);

    ImDrawList* dl = ImGui::GetWindowDrawList();
    dl->AddRectFilled(canvas_min, canvas_max, ImGui::GetColorU32(colors::bg), radius::sm);
    dl->AddRect(canvas_min, canvas_max, ImGui::GetColorU32(colors::border), radius::sm);

    auto to_px = [&](float tx, float ty) {
        // ty puede salir de [0,1] (elastic/bounce); damos algo de margen vertical.
        return ImVec2(canvas_min.x + tx * canvas_size.x,
                      canvas_min.y + (1.0f - ty) * canvas_size.y);
    };

    // Grid 4x4
    ImU32 grid = ImGui::GetColorU32(colors::border);
    for (int i = 1; i < 4; i++) {
        float fx = canvas_min.x + canvas_size.x * (float)i / 4.0f;
        float fy = canvas_min.y + canvas_size.y * (float)i / 4.0f;
        dl->AddLine(ImVec2(fx, canvas_min.y), ImVec2(fx, canvas_max.y), grid);
        dl->AddLine(ImVec2(canvas_min.x, fy), ImVec2(canvas_max.x, fy), grid);
    }

    // Diagonal linear
    dl->AddLine(to_px(0.0f, 0.0f), to_px(1.0f, 1.0f),
                ImGui::GetColorU32(colors::text_dim), 1.0f);

    // Curva
    constexpr int N = 96;
    ImVec2 prev = to_px(0.0f, fn::tween::apply(ease, 0.0f));
    ImU32 col = ImGui::GetColorU32(colors::primary);
    for (int i = 1; i <= N; i++) {
        float x = (float)i / (float)N;
        float y = fn::tween::apply(ease, x);
        ImVec2 cur = to_px(x, y);
        dl->AddLine(prev, cur, col, 2.0f);
        prev = cur;
    }

    // Marker animado
    ImVec2 m = to_px(t_clamped, v);
    dl->AddCircleFilled(m, 5.0f, ImGui::GetColorU32(colors::primary_light));
    dl->AddCircle(m, 6.0f, ImGui::GetColorU32(colors::text), 0, 1.5f);

    // Avanzar cursor
    ImGui::Dummy(canvas_size);

    code_block(
        "#include \"core/tween_curves.h\"\n\n"
        "float k = fn::tween::apply(fn::tween::Ease::OutCubic, t);\n"
        "// o named:\n"
        "float k2 = fn::tween::out_cubic(t);"
    );
}

// ---------------------------------------------------------------------------
// demo_bezier_editor — editor + plot evaluado
// ---------------------------------------------------------------------------

void demo_bezier_editor() {
    using namespace fn_tokens;

    demo_header("bezier_editor", "v1.0.0",
        "Editor visual de curva Bezier cubica (4 puntos). Para diseñar "
        "easing curves custom. p1/p2 son draggable; p0/p3 fijos en (0,0)/(1,1).");

    section("Editor");

    static fn::BezierCurve curve; // identidad por defecto: ease lineal con handles desplazados

    if (ImGui::Button("Reset##bz_reset")) {
        curve = fn::BezierCurve{};
    }
    ImGui::SameLine();
    if (ImGui::Button("Ease-out preset##bz_eo")) {
        curve = {{0,0}, {0.0f, 0.0f}, {0.58f, 1.0f}, {1,1}};
    }
    ImGui::SameLine();
    if (ImGui::Button("Ease-in-out preset##bz_eio")) {
        curve = {{0,0}, {0.42f, 0.0f}, {0.58f, 1.0f}, {1,1}};
    }

    fn::bezier_editor("##bz_editor", curve, ImVec2(220, 220));

    ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
    ImGui::Text("p0=(%.2f,%.2f)  p1=(%.2f,%.2f)  p2=(%.2f,%.2f)  p3=(%.2f,%.2f)",
                curve.p0.x, curve.p0.y, curve.p1.x, curve.p1.y,
                curve.p2.x, curve.p2.y, curve.p3.x, curve.p3.y);
    ImGui::PopStyleColor();

    // Plot evaluation
    section("bezier_eval(curve, t)");
    static float t = 0.0f;
    ImGui::SetNextItemWidth(360.0f);
    ImGui::SliderFloat("t##bz_t", &t, 0.0f, 1.0f, "%.3f");
    float y = fn::bezier_eval(curve, t);
    ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
    ImGui::Text("y(t=%.3f) = %.3f", t, y);
    ImGui::PopStyleColor();

    code_block(
        "#include \"core/bezier_editor.h\"\n\n"
        "static fn::BezierCurve curve;\n"
        "if (fn::bezier_editor(\"##my\", curve, ImVec2(220, 220))) {\n"
        "    // user dragged a control point\n"
        "}\n"
        "float k = fn::bezier_eval(curve, t);"
    );
}

// ---------------------------------------------------------------------------
// demo_timeline — 2 tracks + display
// ---------------------------------------------------------------------------

void demo_timeline() {
    using namespace fn_tokens;
    using fn::tween::Ease;

    demo_header("timeline", "v1.0.0",
        "Timeline tipo DAW: tracks horizontales con keyframes draggable, "
        "scrub con el ruler, play/pause/loop. track_value_at(t) interpola "
        "aplicando la Ease de cada keyframe destino.");

    static fn::TimelineState tl;
    static bool inited = false;
    if (!inited) {
        tl.duration = 4.0f;
        tl.playing  = true;
        tl.tracks.push_back({"hue", {
            {0.0f, 0.0f, Ease::Linear},
            {2.0f, 1.0f, Ease::OutCubic},
            {4.0f, 0.0f, Ease::InOutCubic},
        }});
        tl.tracks.push_back({"amp", {
            {0.0f, 0.2f, Ease::Linear},
            {3.0f, 1.0f, Ease::OutElastic},
        }});
        inited = true;
    }

    // Update
    fn::timeline_update(tl, ImGui::GetIO().DeltaTime);

    // Display values
    section("Live values");
    float hue = fn::track_value_at(tl.tracks[0], tl.current_time);
    float amp = fn::track_value_at(tl.tracks[1], tl.current_time);

    ImGui::PushStyleColor(ImGuiCol_Text, colors::text);
    ImGui::Text("t = %.3fs", tl.current_time);
    ImGui::PopStyleColor();

    auto draw_bar = [&](const char* name, float value, float vmin, float vmax) {
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
        ImGui::Text("%-4s", name);
        ImGui::PopStyleColor();
        ImGui::SameLine();
        ImVec2 cmin = ImGui::GetCursorScreenPos();
        ImVec2 csize = ImVec2(280.0f, 14.0f);
        ImDrawList* dl = ImGui::GetWindowDrawList();
        dl->AddRectFilled(cmin, ImVec2(cmin.x + csize.x, cmin.y + csize.y),
                          ImGui::GetColorU32(colors::surface_active), radius::sm);
        float k = (value - vmin) / (vmax - vmin);
        if (k < 0.0f) k = 0.0f;
        if (k > 1.0f) k = 1.0f;
        dl->AddRectFilled(cmin, ImVec2(cmin.x + csize.x * k, cmin.y + csize.y),
                          ImGui::GetColorU32(colors::primary), radius::sm);
        ImGui::Dummy(csize);
        ImGui::SameLine();
        ImGui::Text("%.3f", value);
    };
    draw_bar("hue", hue, 0.0f, 1.0f);
    draw_bar("amp", amp, 0.0f, 1.0f);

    section("Widget");
    fn::timeline_widget("##gallery_tl", tl, ImVec2(-1, 220));

    code_block(
        "#include \"core/timeline.h\"\n\n"
        "static fn::TimelineState tl;\n"
        "tl.tracks.push_back({\"hue\", {{0,0}, {2,1, fn::tween::Ease::OutCubic}, {4,0}}});\n"
        "tl.duration = 4.0f; tl.playing = true;\n\n"
        "fn::timeline_update(tl, ImGui::GetIO().DeltaTime);\n"
        "float h = fn::track_value_at(tl.tracks[0], tl.current_time);\n"
        "fn::timeline_widget(\"##tl\", tl);"
    );
}

} // namespace gallery
