// Pintado de etiquetas via ImDrawList (issue 0049j). La logica de seleccion
// de candidatos (frustum cull + top-N) vive en graph_labels_select.cpp para
// que sea testeable sin enlazar ImGui.

#include "viz/graph_labels.h"
#include "viz/graph_types.h"
#include "viz/graph_viewport.h"

#include "imgui.h"

#include <cfloat>
#include <vector>

namespace graph {

void graph_labels_draw_at(const GraphData& g,
                          float cam_x, float cam_y, float zoom,
                          float wmin_x, float wmin_y,
                          float w, float h,
                          const LabelPolicy& p,
                          GetLabelFn cb, void* user)
{
    if (!cb)                  return;
    if (g.node_count <= 0)    return;
    if (w <= 0.0f || h <= 0.0f) return;

    // Degrees on-the-fly: O(E) por frame. Cachear si se vuelve hot path.
    static thread_local std::vector<int> degrees;
    degrees.assign((size_t)g.node_count, 0);
    graph_compute_degrees(g, degrees.data());

    // Cap duro: max_visible + |always_*|. |always_*| <= node_count.
    const int max_total = p.max_visible + g.node_count;
    static thread_local std::vector<int> indices;
    indices.resize((size_t)max_total);

    const int n_labels = graph_labels_select(
        g, p, cam_x, cam_y, zoom, w, h,
        degrees.data(), indices.data(), max_total);
    if (n_labels <= 0) return;

    ImDrawList* dl   = ImGui::GetWindowDrawList();
    ImFont*     font = ImGui::GetFont();
    if (!dl || !font) return;

    const float cx = wmin_x + w * 0.5f;
    const float cy = wmin_y + h * 0.5f;

    for (int k = 0; k < n_labels; ++k) {
        const int idx = indices[k];
        const GraphNode& n = g.nodes[idx];
        const char* text = cb(idx, user);
        if (!text || !*text) continue;

        const float vx = (n.x - cam_x) * zoom + cx;
        const float vy = (n.y - cam_y) * zoom + cy;

        const float node_px = resolve_node_size(n, g.types, g.type_count) * zoom;
        const float ox = node_px * 0.5f + p.padding_x;
        const float oy = -p.font_size * 0.5f;

        const ImVec2 ts = font->CalcTextSizeA(p.font_size, FLT_MAX, 0.0f, text);
        const ImVec2 a(vx + ox - p.padding_x, vy + oy - p.padding_y);
        const ImVec2 b(vx + ox + ts.x + p.padding_x, vy + oy + ts.y + p.padding_y);

        dl->AddRectFilled(a, b, p.bg_color, 2.0f);
        dl->AddText(font, p.font_size, ImVec2(vx + ox, vy + oy),
                    p.color, text);
    }
}

void graph_labels_draw(const GraphData& g, const GraphViewportState& s,
                       const LabelPolicy& p, GetLabelFn cb, void* user)
{
    const ImVec2 wmin = ImGui::GetItemRectMin();
    const ImVec2 wmax = ImGui::GetItemRectMax();
    const float w = wmax.x - wmin.x;
    const float h = wmax.y - wmin.y;
    graph_labels_draw_at(g, s.cam_x, s.cam_y, s.zoom,
                         wmin.x, wmin.y, w, h, p, cb, user);
}

} // namespace graph
