// editor_geometry.h — PURE view geometry + hit-test for the VST3 IPlugView LICE
// editor (Phase S1). NO VST3, NO REAPER, NO SWELL/LICE types at the boundary.
//
// The IPlugView shell (reasampler_editor.cpp) owns the window/bitmap/SWELL plumbing
// and is DAW-verified; this module holds the fiddly rectangle math and hit-testing so
// it can be unit-tested outside the DAW — the mirror of how bank_grid / mode_switch /
// tab_strip split their layout math out of the panel shell.
//
// The spike's editor is deliberately trivial (a title band + one clickable button),
// enough to PROVE the host->draw/hit-test event routing works. As the real editor
// (S4/S5) grows, its layout math accretes here, not in the shell.

#pragma once

namespace reasampler::vst {

// A plain integer rectangle. left/top inclusive, right/bottom exclusive — the same
// half-open convention LICE/SWELL RECTs use, kept REAPER-free here.
struct Rect {
    int left = 0;
    int top = 0;
    int right = 0;
    int bottom = 0;

    int width() const { return right - left; }
    int height() const { return bottom - top; }
};

// Returns true if (x, y) falls inside r under the half-open convention
// (left <= x < right, top <= y < bottom). A zero-or-negative-area rect contains
// nothing.
bool contains(const Rect& r, int x, int y);

// The regions the spike editor draws, derived from the current view size. All are
// clamped to the client area so a degenerate (too-small) view never yields a region
// that spills outside the surface.
struct EditorLayout {
    Rect titleBar;   // top band: the plugin name + a live-state readout
    Rect button;     // a single clickable button (proves hit-test routing)
    Rect canvas;     // the remaining surface below the title bar
};

// Divide a (w x h) client area into the spike editor's regions. Pure: the same
// inputs always yield the same layout. Guards tiny sizes — every returned rect stays
// within [0,w] x [0,h], and the button never overhangs the canvas.
EditorLayout layoutEditor(int w, int h);

// The editor's hit-test targets. kNone means the point landed on inert surface.
enum class HitTarget {
    kNone,
    kButton,
};

// Classify a click at (x, y) against a layout. The button wins only when the point is
// inside the button rect; everything else (including the title bar and empty canvas)
// is kNone in the spike.
HitTarget hitTest(const EditorLayout& layout, int x, int y);

// --- Sample-selection list (S4 Tier-0 UI) -----------------------------------
//
// The Tier-0 editor lists the bank's samples as a vertical stack of fixed-height rows
// below the title bar; clicking a row selects that sample. This is the pure geometry:
// the row rectangles and the point->row hit-test, unit-tested outside the DAW while the
// shell draws the names and routes the click into the processor's reloadFromBank.

// The fixed row height (px) for one sample entry. Exposed so the shell and tests agree.
inline constexpr int kSampleRowHeight = 22;

// The rectangle for row `index` (0-based) of the sample list, laid out top-down inside
// the layout's canvas. Rows beyond what the canvas can show are still computed (the
// shell clips at paint time); a negative index yields an empty rect. Pure.
Rect sampleRowRect(const EditorLayout& layout, int index);

// The row index a click at (x, y) lands on, given `rowCount` rows, or -1 for a click
// outside the list (above the first row, past the last, or on the title bar). Pure.
int sampleRowHitTest(const EditorLayout& layout, int rowCount, int x, int y);

// --- Keymap editor (S5 Tier-1 UI) -------------------------------------------
//
// The Tier-1 editor splits the canvas into a LEFT bank-sample list (the same rows as
// Tier 0, reused for the "sample to add / fallback pick") and a RIGHT zone panel listing
// the performance map's zones. An "Add Zone" button sits at the top of the zone panel;
// each zone row carries small nudge/delete controls so the user can set the range and
// root note without a text field (LICE has no native numeric entry). All rectangle math
// is here so the shell only draws + routes — the mirror of the sample-list split above.

// Fixed metrics for the zone panel, exposed so the shell and tests agree.
inline constexpr int kZoneRowHeight = 24;
inline constexpr int kZonePanelFraction = 2;  // zone panel gets the RIGHT 1/2 of the canvas
inline constexpr int kZoneCtrlWidth = 20;     // width of one nudge/delete mini-button
inline constexpr int kAddZoneHeight = 22;     // the "Add Zone" button band height

// The keymap editor's regions, derived from the (w x h) client area. All clamp to the
// canvas so a degenerate view yields in-bounds rects.
struct KeymapEditorLayout {
    EditorLayout base;   // title bar + canvas (the sample list uses base.canvas.left half)
    Rect sampleList;     // LEFT column: the bank-sample rows (sampleRowRect is relative here)
    Rect zonePanel;      // RIGHT column: the "Add Zone" button + the zone rows
    Rect addZoneButton;  // top of the zone panel
    Rect zoneRowArea;    // below addZoneButton: where zone rows stack
};

KeymapEditorLayout layoutKeymapEditor(int w, int h);

// The rectangle for bank-sample row `index` inside the LEFT sample list column of a
// keymap layout. Same fixed height as the Tier-0 list; laid out top-down inside
// sampleList. Negative index -> empty. Pure.
Rect keymapSampleRowRect(const KeymapEditorLayout& layout, int index);

// The bank-sample row a click lands on inside the left list, or -1 outside it. Pure.
int keymapSampleRowHitTest(const KeymapEditorLayout& layout, int rowCount, int x, int y);

// The rectangle for zone row `index` inside the zone panel's zoneRowArea. Negative
// index -> empty. Pure.
Rect zoneRowRect(const KeymapEditorLayout& layout, int index);

// A zone row's interactive fields. The row is a horizontal strip: a label on the left,
// then seven fixed-width mini-buttons on the right (left-to-right: low-, low+, high-, high+,
// root-, root+, delete). kZoneNone means the click missed a control
// (e.g. on the label) — the shell may still treat that as "select this zone".
enum class ZoneField {
    kZoneNone,
    kLowDown,
    kLowUp,
    kHighDown,
    kHighUp,
    kRootDown,
    kRootUp,
    kDelete,
};

// The result of hit-testing a click against the zone rows: which zone row (or -1) and
// which field within it. A click on the "Add Zone" button is reported separately by
// addZoneHitTest — this covers only the zone rows.
struct ZoneHit {
    int zoneIndex = -1;
    ZoneField field = ZoneField::kZoneNone;
};

// Classify a click at (x, y) against `zoneCount` zone rows. Returns {-1, kZoneNone} for a
// click outside every zone row. Within a row, the seven mini-buttons occupy fixed-width
// slots on the right edge (left-to-right: low-, low+, high-, high+, root-, root+, delete);
// a click left of those slots is {index, kZoneNone} (the label area — "select"). Pure.
ZoneHit zoneHitTest(const KeymapEditorLayout& layout, int zoneCount, int x, int y);

// True if (x, y) lands on the "Add Zone" button. Pure.
bool addZoneHitTest(const KeymapEditorLayout& layout, int x, int y);

}  // namespace reasampler::vst
