// action_bar — pure implementation. See action_bar.h.

#include "core/ui/action_bar.h"

#include <cstddef>

namespace reasampler::ui {

namespace {

int totalButtons(const std::vector<ClusterSpec>& clusters) {
    int n = 0;
    for (const ClusterSpec& c : clusters)
        if (c.count > 0) n += c.count;
    return n;
}

void fillTextRects(ActionBarSlot& s, const ActionBarSpec& /*spec*/) {
    const int hpad = 4;
    const int innerX = s.x + hpad;
    const int innerW = s.width - 2 * hpad;
    if (innerW <= 0) return;
    s.labelX = innerX; s.labelY = s.y; s.labelW = innerW; s.labelH = s.height;
}

// The one placement routine; computeBarSlots and hitTestActionBar both drive it so draw and
// hit-test can't drift apart.
std::vector<ActionBarSlot> tile(const ActionBarRect& bar,
                                const std::vector<ClusterSpec>& clusters,
                                const ActionBarSpec& spec, int visible) {
    std::vector<ActionBarSlot> slots;
    if (visible <= 0) return slots;
    slots.reserve(static_cast<std::size_t>(visible));

    const int top = bar.y + spec.verticalInset;
    const int btnH = bar.height - 2 * spec.verticalInset;
    if (btnH <= 0) return slots;

    int cursorX = bar.x + spec.sidePad;
    int flatIndex = 0;
    int placed = 0;
    bool firstClusterEmitted = false;

    for (const ClusterSpec& c : clusters) {
        if (c.count <= 0) continue;
        if (placed >= visible) break;

        if (firstClusterEmitted) cursorX += spec.clusterGap;
        firstClusterEmitted = true;

        for (int i = 0; i < c.count; ++i, ++flatIndex) {
            if (placed >= visible) return slots;
            if (i > 0) cursorX += spec.buttonGap;

            ActionBarSlot s;
            s.index = flatIndex;
            s.cluster = c.cluster;
            s.x = cursorX;
            s.y = top;
            s.width = spec.buttonWidth;
            s.height = btnH;
            fillTextRects(s, spec);
            slots.push_back(s);

            cursorX += spec.buttonWidth;
            ++placed;
        }
    }
    return slots;
}

// Mirrors tile()'s advance math so fit and layout agree.
int rightEdgeFor(const ActionBarRect& bar, const std::vector<ClusterSpec>& clusters,
                 const ActionBarSpec& spec, int visible) {
    if (visible <= 0) return bar.x + spec.sidePad;
    int cursorX = bar.x + spec.sidePad;
    int placed = 0;
    bool firstClusterEmitted = false;
    for (const ClusterSpec& c : clusters) {
        if (c.count <= 0) continue;
        if (placed >= visible) break;
        if (firstClusterEmitted) cursorX += spec.clusterGap;
        firstClusterEmitted = true;
        for (int i = 0; i < c.count; ++i) {
            if (placed >= visible) return cursorX;
            if (i > 0) cursorX += spec.buttonGap;
            cursorX += spec.buttonWidth;
            ++placed;
            if (placed >= visible) return cursorX;
        }
    }
    return cursorX;
}

} // namespace

BarFit computeBarFit(const ActionBarRect& bar, const std::vector<ClusterSpec>& clusters,
                     const ActionBarSpec& spec) {
    BarFit fit;
    const int total = totalButtons(clusters);
    if (total <= 0 || bar.width <= 0 || bar.height <= 0 || spec.buttonWidth <= 0) {
        fit.hiddenCount = total > 0 ? total : 0;
        return fit;
    }

    const int usableRight = bar.x + bar.width - spec.sidePad;
    int visible = 0;
    for (int cand = 1; cand <= total; ++cand) {
        if (rightEdgeFor(bar, clusters, spec, cand) <= usableRight)
            visible = cand;
        else
            break;
    }
    fit.visibleCount = visible;
    fit.hiddenCount = total - visible;
    return fit;
}

std::vector<ActionBarSlot> computeBarSlots(const ActionBarRect& bar,
                                           const std::vector<ClusterSpec>& clusters,
                                           const ActionBarSpec& spec) {
    if (bar.width <= 0 || bar.height <= 0 || spec.buttonWidth <= 0) return {};
    const BarFit fit = computeBarFit(bar, clusters, spec);
    return tile(bar, clusters, spec, fit.visibleCount);
}

int hitTestActionBar(int px, int py, const ActionBarRect& bar,
                     const std::vector<ClusterSpec>& clusters, const ActionBarSpec& spec) {
    if (bar.height <= 0 || bar.width <= 0) return -1;
    if (px < bar.x || px >= bar.x + bar.width ||
        py < bar.y || py >= bar.y + bar.height)
        return -1;

    const std::vector<ActionBarSlot> slots = computeBarSlots(bar, clusters, spec);
    for (const ActionBarSlot& s : slots) {
        if (px >= s.x && px < s.x + s.width && py >= s.y && py < s.y + s.height)
            return s.index;
    }
    return -1;
}

} // namespace reasampler::ui
