#pragma once
#include "core/ui/rect.h"
// component_geometry — geometry + hit-test math for the shared drawing kit's generic components:
// a button box, a slider's track/handle, and a list row. bank_grid / tab_strip / prune_button
// stay the source of truth for the surfaces they own; this carries only the reusable component
// shapes the kit's drawButton / drawSlider / drawListRow draw against.

namespace reasampler::ui {

// A generic pixel box, top-left origin. empty() means "nothing to draw / hit".
using KitBox = Rect;

// Half-open bounds [x, x+width) x [y, y+height); an empty box claims no point.
bool hitTestBox(int px, int py, const KitBox& box);

// --- Button ------------------------------------------------------------------

// A button drawn inside a host cell, inset by uniform padding so it reads as raised rather than
// full-bleed. Distinct from prune_button, which owns its own placement within its strip.
struct KitButtonBox {
    KitBox box;

    bool operator==(const KitButtonBox& o) const { return box == o.box; }
};

// Button box inside `cell`, inset uniformly by `padding`. Returns an empty box (suppressed) when
// the cell is degenerate or padding would collapse it to zero-or-negative area. padding < 0 -> 0.
KitButtonBox computeButtonBox(const KitBox& cell, int padding);

// --- Slider (horizontal) -----------------------------------------------------

// track spans the control width, inset at both ends by half the handle width so the handle never
// clips past it. handle is centered on the track, positioned by the normalized value.
struct SliderGeometry {
    KitBox track;
    KitBox filled;  // filled portion from track's left up to the handle center
    KitBox handle;

    bool operator==(const SliderGeometry& o) const {
        return track == o.track && filled == o.filled && handle == o.handle;
    }
};

// Lays out a horizontal slider inside `control` for normalized `value` in [0, 1] with a square
// handle of side `handleSize`, track vertically centered at `trackThickness`. value clamps to
// [0, 1]. Returns all-empty boxes when the control is too small to host the handle, or when
// handleSize/trackThickness <= 0.
SliderGeometry computeSlider(const KitBox& control, double value,
                             int handleSize, int trackThickness);

// Inverse of computeSlider's handle placement (a track-jump click): normalized value [0, 1] a
// click at px maps to. Clamps to [0, 1] outside the track; 0.0 for a degenerate/too-small
// control. py unused (horizontal slider maps X only) — caller gates with hitTestBox(control) first.
double sliderValueAt(int px, const KitBox& control, int handleSize);

// --- List row ----------------------------------------------------------------

// One selectable row: full-width, fixed height, stacked from the list's top by index (no scroll
// — caller offsets the list origin for that).
struct ListRowBox {
    int index = 0;
    KitBox box;

    bool operator==(const ListRowBox& o) const {
        return index == o.index && box == o.box;
    }
};

// Row box for `index` inside `list` at `rowHeight` per row; rows stack from list.y. Empty when
// the list is degenerate, rowHeight <= 0, index < 0, or the row falls entirely below the list's
// bottom. A partially-visible last row IS returned — caller clips the draw.
ListRowBox computeListRow(const KitBox& list, int index, int rowHeight);

// Index of the row a point lands on, or -1 for a miss (outside bounds, or in the empty tail past
// `rowCount` rows). rowCount bounds the hit so blank space past the last row is a clean miss.
int hitTestListRow(int px, int py, const KitBox& list, int rowHeight, int rowCount);

// --- Waveform columns --------------------------------------------------------

// Pixel columns drawWaveform renders inside `box` (its fixed 2px side insets), never negative.
// Pass directly as peaks::computeEnvelope's binCount — one bin per column is correct resolution;
// overbinning doesn't improve render quality and wastes memory/CPU.
int waveformColumnCount(const KitBox& box);

// One channel band's shared vertical metrics: the zero line every column mirrors about, and the
// pixel height a full-scale amplitude reaches (inset so a peak keeps the band's breathing room).
struct WaveformBand {
    int    top      = 0;
    int    height   = 0;
    int    midY     = 0;
    double halfSpan = 0.0;
};
WaveformBand waveformBand(int bandTop, int bandHeight);

// The vertical extents of one column: the integer edges of the filled span, and the same edges
// at sub-pixel precision for the antialiased outline stroke that joins a column's extremes to
// its neighbour's. Both take the band clamp, so the stroke cannot leave the band the fill is
// confined to. Amplitudes arrive already through the display curve.
struct WaveformColumnSpan {
    int    top     = 0;
    int    bottom  = 0;
    double topF    = 0.0;
    double bottomF = 0.0;
};

// Truncation is applied to the SCALED AMPLITUDE and then mirrored about midY — never to the
// resulting y, which would round the two edges in opposite directions and leave a column with
// |max| == |min| a pixel taller above the zero line than below.
WaveformColumnSpan waveformColumnSpan(const WaveformBand& band,
                                      double compressedMax, double compressedMin);

} // namespace reasampler::ui
