// bake_hold.cpp — see bake_hold.h. Pure math; no host types.

#include "core/instrument/ui/bake_hold.h"

#include <algorithm>
#include <array>

namespace reasampler::instrument::ui {

namespace {

constexpr int kLastSlot = note::kDivisionCount - 1;

using Order = std::array<int, note::kDivisionCount>;

// Slot -> picker index, sorted by LENGTH. The ladder's own order is presentation order, in
// which a rung's triplet is shorter than the previous rung's dotted (musical_division.cpp) —
// so addressing it directly makes a knob whose whole meaning is duration shorten the note at
// every rung boundary. Built once; every length on the ladder is distinct, so the sort is total.
const Order& bySlot() {
    static const Order order = [] {
        Order a{};
        for (int i = 0; i < note::kDivisionCount; ++i) a[static_cast<std::size_t>(i)] = i;
        std::sort(a.begin(), a.end(), [](int l, int r) {
            return note::divisionBeats(note::divisionAt(l)) <
                   note::divisionBeats(note::divisionAt(r));
        });
        return a;
    }();
    return order;
}

// The inverse: picker index -> slot.
const Order& toSlot() {
    static const Order inverse = [] {
        Order a{};
        const Order& forward = bySlot();
        for (int slot = 0; slot < note::kDivisionCount; ++slot)
            a[static_cast<std::size_t>(forward[static_cast<std::size_t>(slot)])] = slot;
        return a;
    }();
    return inverse;
}

}  // namespace

note::Division bakeHoldFromNorm(double norm) {
    if (!(norm > 0.0)) return note::divisionAt(bySlot()[0]);  // also catches NaN
    if (norm >= 1.0) return note::divisionAt(bySlot()[kLastSlot]);
    // Round to nearest so each rung owns an equal slice of the knob's travel.
    const int slot = static_cast<int>(norm * kLastSlot + 0.5);
    return note::divisionAt(bySlot()[static_cast<std::size_t>(slot)]);
}

double bakeHoldNorm(note::Division hold) {
    const int slot = toSlot()[static_cast<std::size_t>(note::divisionIndex(hold))];
    return static_cast<double>(slot) / static_cast<double>(kLastSlot);
}

}  // namespace reasampler::instrument::ui
