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

#include "embed_strip.h"

#include <algorithm>

namespace reasampler::vst {

namespace {

// Clamp a MIDI note to [0, kEmbedKeyCount-1].
int clampNote(int n) {
    if (n < 0) return 0;
    if (n > kEmbedKeyCount - 1) return kEmbedKeyCount - 1;
    return n;
}

// Map a key boundary in [0, kEmbedKeyCount] to an x pixel inside a band of the given
// left/width. keyEdge is a boundary (0..128), so keyEdge==128 maps to the band's right.
// Integer math, floored — a zone's left uses floor(low) and its right uses floor(high+1),
// which tiles adjacent zones without a seam.
int keyEdgeToX(int bandLeft, int bandWidth, int keyEdge) {
    if (keyEdge <= 0) return bandLeft;
    if (keyEdge >= kEmbedKeyCount) return bandLeft + bandWidth;
    return bandLeft + (keyEdge * bandWidth) / kEmbedKeyCount;
}

}  // namespace

EmbedLayout layoutEmbed(int w, int h) {
    const int cw = std::max(0, w);
    const int ch = std::max(0, h);

    EmbedLayout out;

    // The level band takes a fixed height at the bottom, but never so much that the keymap
    // above it falls below its minimum (or that the band exceeds the area). On a very short
    // area the band yields to the keymap entirely.
    int bandH = std::min(kEmbedLevelBandHeight, ch);
    if (ch - bandH < kEmbedKeymapMinHeight) {
        bandH = std::max(0, ch - kEmbedKeymapMinHeight);
    }
    const int keymapBottom = ch - bandH;

    out.keymap = Rect{0, 0, cw, keymapBottom};
    out.levelBand = Rect{0, keymapBottom, cw, ch};
    return out;
}

Rect zoneSegmentRect(const EmbedLayout& layout, int lowNote, int highNote) {
    const Rect& band = layout.keymap;
    const int bandWidth = std::max(0, band.width());

    int lo = clampNote(lowNote);
    int hi = clampNote(highNote);
    if (lo > hi) lo = hi;  // defensive: a malformed zone collapses rather than inverts

    const int leftX = keyEdgeToX(band.left, bandWidth, lo);
    const int rightX = keyEdgeToX(band.left, bandWidth, hi + 1);
    return Rect{leftX, band.top, std::max(leftX, rightX), band.bottom};
}

int zoneAtPoint(const EmbedLayout& layout, const EmbedZone* zones, int zoneCount, int x,
                int y) {
    if (zoneCount <= 0 || zones == nullptr) return -1;
    if (!contains(layout.keymap, x, y)) return -1;
    // First covering zone in draw order wins (first-match, mirroring the core's resolve).
    for (int i = 0; i < zoneCount; ++i) {
        const Rect r = zoneSegmentRect(layout, zones[i].lowNote, zones[i].highNote);
        if (contains(r, x, y)) return i;
    }
    return -1;  // on the band but on an uncovered key
}

Rect levelFillRect(const EmbedLayout& layout, double level) {
    const Rect& band = layout.levelBand;
    if (band.width() <= 0 || band.height() <= 0) return Rect{};
    double l = level;
    if (l < 0.0) l = 0.0;
    if (l > 1.0) l = 1.0;
    const int fillW = static_cast<int>(l * band.width());
    if (fillW <= 0) return Rect{};
    return Rect{band.left, band.top, band.left + fillW, band.bottom};
}

}  // namespace reasampler::vst
