// browser_scroll.h — scroll + type-to-filter geometry layered over capture_browser. Mirror
// of capture_browser/editor_geometry; the shell draws the clipped card window, scrollbar,
// and search field, and routes wheel/drag/keystrokes into these functions.
//
// capture_browser lays out every card top-down and the shell clips at the browser bottom —
// a bank longer than the panel has no way to reach the rest. This module adds scroll (a
// vertical pixel offset with max-offset clamp, visible-row window, scrollbar thumb, and
// thumb-drag<->offset mapping) and search (a case-insensitive name-substring filter that
// composes with capture_browser's bank filter — the shell applies the bank filter first,
// then this search narrows within it).
//
// Holds no card data and draws nothing — knows only the browser layout, counts, and the
// scroll offset the shell owns as transient UI state.

#pragma once

#include <string>
#include <vector>

#include "core/instrument/ui/capture_browser.h"  // BrowserLayout, cardCellRect, kBrowserCardHeight, Rect

namespace reasampler::instrument::ui {

// Width of the vertical scrollbar gutter at the grid's right edge. When content fits (no
// scroll needed), scrollThumbRect returns empty and the shell may reclaim the gutter.
inline constexpr int kScrollbarWidth = 10;

// Height of the type-to-filter search box the shell draws above the tab strip.
// capture_browser's tab strip + grid sit below this band.
inline constexpr int kSearchBoxHeight = 22;

// Total pixel height the card grid needs for `cardCount` cards at `layout`'s column
// count: rows (ceil(cardCount / columns)) times the fixed cell height.
int scrollContentHeight(const BrowserLayout& layout, int cardCount);

// Maximum scroll offset: content height minus visible grid height, floored at 0.
int scrollMaxOffset(const BrowserLayout& layout, int cardCount);

// Clamps a proposed scroll offset into [0, scrollMaxOffset].
int clampScrollOffset(const BrowserLayout& layout, int cardCount, int proposedOffset);

// Half-open range of card indices [first, last) at least partially visible at scroll
// `offset` (assumed pre-clamped). The shell draws only these cards.
struct VisibleRange {
    int first = 0;
    int last = 0;
};
VisibleRange visibleCardRange(const BrowserLayout& layout, int cardCount, int offset);

// Cell rect of card `index` shifted up by the scroll offset (the shell still adds the
// browser sub-area origin).
Rect scrolledCardCellRect(const BrowserLayout& layout, int index, int offset);

// Vertical scrollbar thumb rect within the grid's right-edge gutter, sized proportional
// to the visible fraction and positioned proportional to the scroll offset. Empty when
// the content fits. A minimum thumb height keeps a tiny thumb grabbable on a long bank.
Rect scrollThumbRect(const BrowserLayout& layout, int cardCount, int offset);

// Maps a thumb-drag to a new (clamped) scroll offset: `startOffset` shifted by the pixel
// delta scaled from thumb-track pixels to content pixels. A degenerate track or fitting
// content pins to startOffset.
int thumbDragToOffset(const BrowserLayout& layout, int cardCount, int startOffset, int dyPixels);

// Search-box rect: full-width band of height kSearchBoxHeight at the top of the browser
// area. `w` is the browser sub-area width; the shell adds its origin.
Rect searchBoxRect(int w);

// True iff `name` contains `query` as a case-insensitive ASCII substring. An empty query
// matches everything.
bool nameMatchesQuery(const std::string& name, const std::string& query);

// Narrows a list of display `names` to the indices whose name matches `query`, preserving
// order. An empty query returns every index. Kept name-only (indices, not card structs)
// so this module stays free of the sample_map/bank_book chain — the shell applies the
// bank filter first, then feeds the surviving display names here.
std::vector<int> filterNameIndices(const std::vector<std::string>& names,
                                   const std::string& query);

// --- Browse-modal top-level regions --------------------------------------------
//
// A title band with a Back button, the search box, the browser sub-area (tabs + card
// grid — layoutBrowser's origin), and a footer with Cancel / Load-confirm. The picker
// covers the full window. Homed here (not editor_geometry) because the search-box
// height feeds it.
struct BrowseModal {
    Rect title;
    Rect back;
    Rect search;
    Rect content;   // browser sub-area (tabs + grid) — layoutBrowser's origin
    Rect cancel;
    Rect confirm;
};
BrowseModal computeBrowseModal(int w, int h);

}  // namespace reasampler::instrument::ui
