#include "viz/voronoi.h"

#include <cmath>

namespace {

inline int nearest_seed(float px, float py, const ImVec2* seeds, int n) {
    int   best = 0;
    float bd2  = 1e30f;
    for (int i = 0; i < n; i++) {
        float dx = px - seeds[i].x;
        float dy = py - seeds[i].y;
        float d2 = dx * dx + dy * dy;
        if (d2 < bd2) { bd2 = d2; best = i; }
    }
    return best;
}

} // namespace

std::vector<VoronoiCell> voronoi_layout(const ImVec2* seeds, int n, ImVec2 /*region*/) {
    std::vector<VoronoiCell> out;
    if (!seeds || n <= 0) return out;
    out.resize(n);
    for (int i = 0; i < n; i++) {
        out[i].seed    = seeds[i];
        out[i].color   = IM_COL32(120, 144, 252, 230);
        out[i].polygon.clear();
    }
    return out;
}

void voronoi(const char*  id,
             const ImVec2* seeds,
             int           n,
             const ImU32*  colors,
             ImVec2        size) {
    ImGui::PushID(id);

    ImVec2 avail = ImGui::GetContentRegionAvail();
    float  W     = (size.x > 0.0f) ? size.x : avail.x;
    float  H     = (size.y > 0.0f) ? size.y : 200.0f;

    ImVec2 origin = ImGui::GetCursorScreenPos();
    ImGui::Dummy(ImVec2(W, H));

    if (!seeds || n <= 0) { ImGui::PopID(); return; }

    ImDrawList* dl = ImGui::GetWindowDrawList();

    // Raster: tile de 4x4 pixels. Region <= ~600x400 con N<=200 -> ~150*100 = 15000 calls,
    // dentro de presupuesto frame.
    const float tile = 4.0f;
    int   cols = (int)std::ceil(W / tile);
    int   rows = (int)std::ceil(H / tile);

    for (int ry = 0; ry < rows; ry++) {
        float py = (ry + 0.5f) * tile;
        if (py > H) py = H;
        for (int rx = 0; rx < cols; rx++) {
            float px = (rx + 0.5f) * tile;
            if (px > W) px = W;
            int   idx = nearest_seed(px, py, seeds, n);
            ImU32 col = colors ? colors[idx] : IM_COL32(120, 144, 252, 230);

            ImVec2 a(origin.x + rx * tile, origin.y + ry * tile);
            ImVec2 b(origin.x + std::min((rx + 1) * tile, W),
                     origin.y + std::min((ry + 1) * tile, H));
            dl->AddRectFilled(a, b, col);
        }
    }

    // Borde y seeds
    dl->AddRect(origin, ImVec2(origin.x + W, origin.y + H),
                IM_COL32(80, 80, 90, 220), 0.0f, 0, 1.0f);
    for (int i = 0; i < n; i++) {
        ImVec2 c(origin.x + seeds[i].x, origin.y + seeds[i].y);
        dl->AddCircleFilled(c, 2.5f, IM_COL32(20, 22, 28, 255));
        dl->AddCircleFilled(c, 1.5f, IM_COL32(245, 246, 250, 255));
    }

    ImGui::PopID();
}
