// Demo del primitivo viz/mesh_viewer.
// Genera un cubo procedural in-line, lo sube al GPU, y permite cargar un
// .obj desde un path ingresado en un text input.

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

#include "viz/mesh_viewer.h"
#include "gfx/mesh_obj_load.h"
#include "gfx/mesh_gpu.h"
#include "core/orbit_camera.h"

#include <imgui.h>
#include <cstring>
#include <string>

namespace gallery {

namespace {

const char* kCubeObj =
    "v -1 -1 -1\nv 1 -1 -1\nv 1 1 -1\nv -1 1 -1\n"
    "v -1 -1 1\nv 1 -1 1\nv 1 1 1\nv -1 1 1\n"
    "f 4 3 2 1\n"   // back   (-Z) — winding for outward normal
    "f 5 6 7 8\n"   // front  (+Z)
    "f 1 2 6 5\n"   // bottom (-Y)
    "f 8 7 3 4\n"   // top    (+Y)
    "f 5 8 4 1\n"   // left   (-X)
    "f 2 3 7 6\n";  // right  (+X)

struct State {
    fn::gfx::MeshGpu        mesh{};
    fn::core::OrbitCamera   cam{};
    char                    path[512] = "";
    std::string             status;
    bool                    wireframe = false;
    bool                    initialized = false;
};

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

void load_cube() {
    auto& s = state();
    if (s.mesh.ok()) fn::gfx::mesh_gpu_destroy(s.mesh);
    auto cpu = fn::gfx::mesh_obj_parse(kCubeObj, std::strlen(kCubeObj));
    s.mesh = fn::gfx::mesh_gpu_upload(cpu);
    s.status = s.mesh.ok()
        ? ("loaded cube: " + std::to_string(s.mesh.index_count / 3) + " tris")
        : "cube upload failed";
}

void load_from_path() {
    auto& s = state();
    if (!s.path[0]) { s.status = "path is empty"; return; }
    auto cpu = fn::gfx::mesh_obj_load(s.path);
    if (cpu.positions.empty()) { s.status = "parse/read failed"; return; }
    if (s.mesh.ok()) fn::gfx::mesh_gpu_destroy(s.mesh);
    s.mesh = fn::gfx::mesh_gpu_upload(cpu);
    s.status = s.mesh.ok()
        ? ("loaded: " + std::to_string(s.mesh.index_count / 3) + " tris")
        : "upload failed";
}

} // namespace

void demo_mesh_viewer() {
    demo_header("mesh_viewer", "v1.0.0",
        "Visualizador 3D para inspeccion de geometria. Composicion de "
        "mesh_obj_load (parser .obj puro) + mesh_gpu (upload VAO/VBO/EBO) + "
        "orbit_camera (drag/wheel) + mesh_viewer (FBO + ImGui::Image + Lambert).");

    auto& s = state();
    if (!s.initialized) {
        load_cube();
        s.initialized = true;
    }

    // Controls row.
    if (ImGui::Button("Reload cube")) load_cube();
    ImGui::SameLine();
    ImGui::Checkbox("Wireframe", &s.wireframe);
    ImGui::SameLine();
    ImGui::TextDisabled("|");
    ImGui::SameLine();
    ImGui::SetNextItemWidth(360);
    ImGui::InputTextWithHint("##obj_path", "absolute path to .obj", s.path, sizeof(s.path));
    ImGui::SameLine();
    if (ImGui::Button("Load .obj")) load_from_path();

    ImGui::TextDisabled("status: %s | tris: %d | drag to orbit, wheel to zoom",
                        s.status.c_str(),
                        s.mesh.ok() ? s.mesh.index_count / 3 : 0);

    ImGui::Separator();

    fn::viz::MeshViewerConfig cfg{};
    cfg.mesh      = &s.mesh;
    cfg.cam       = &s.cam;
    cfg.size      = ImVec2(-1.0f, 480.0f);
    cfg.color     = IM_COL32(160, 200, 255, 255);
    cfg.wireframe = s.wireframe;
    fn::viz::mesh_viewer("##gallery_mesh_viewer", cfg);
}

} // namespace gallery
