#pragma once
#include "core/ui/rect.h"
// drag_out — decision logic behind the bank_panel's native OS drag-out. OLE/SWELL initiation and
// the panel gesture hook stay in the shell (drag_out_win.* + shell/panel/panel_drag.cpp).
//
// Gesture boundary: the panel's own internal drag (press a selected cell, drop onto a pool/bank
// region or tab) lives entirely inside the panel client rect. The moment the pointer LEAVES that
// rect while a drag is armed with samples, the gesture becomes OS-bound — dragged out to another
// window/Explorer/DAW. A single-capture drag that leaves the rect but is still over REAPER's own
// UI is instead an InstrumentDrop (heading for a track's FX button); do not regress this boundary.
//
// Path-list assembly: turns armed sample ids into the absolute path list the OS drop carries
// (Windows CF_HDROP / macOS file-list pasteboard) — set algebra only; the shell resolves each id
// to its on-disk bank file. No temp files; copy-only is enforced at the OS layer (drag_out_win).

#include <string>
#include <vector>

namespace reasampler::ui {

// --- Gesture boundary ---------------------------------------------------------

// The panel's client rect, own client coords, top-left origin. Half-open: [x, x+width) x
// [y, y+height).
using PanelClientRect = Rect;

// Live drag state reduced to what the boundary decision needs. Pre-threshold "armed but not yet
// dragging" is not a drag for this decision.
struct DragState {
    bool dragging = false;        // threshold crossed; a drag is in progress
    bool hasArmedSamples = false; // payload holds >= 1 sample id
    bool singleCapture = false;   // payload holds EXACTLY one sample (arms InstrumentDrop)
    bool overReaperUi = false;    // pointer is over REAPER's own UI (shell-supplied)
};

// What the shell should do with the drag given the current pointer position.
enum class DragGesture {
    None,           // no drag under way, or an empty payload
    Internal,       // dragging inside the panel — bank-to-bank move/copy
    InstrumentDrop, // single-capture drag left the panel but is over REAPER's UI — shell
                    // hover-tracks the TCP FX button; on release adds a preloaded instance
    OsDrag,         // dragging with samples, pointer left REAPER entirely — hand to the OS
};

// Decides the gesture for a drag at pointer (px, py) over `client`, given `state`. Position-only
// + state-only (no hidden state), so re-entry back inside always returns Internal.
DragGesture decideGesture(int px, int py, const PanelClientRect& client,
                          const DragState& state);

// --- Path-list assembly -------------------------------------------------------

// One armed sample reduced to what path assembly needs: the shell-resolved absolute path (empty
// if unresolvable) and whether it exists on disk.
struct ResolvedSample {
    std::string absolutePath;
    bool fileExists = false;
};

// Outcome of assembling the drag's path list. An empty `paths` means nothing draggable — do not
// start a drag.
struct PathList {
    std::vector<std::string> paths;  // de-duped, existing files, first-seen order
    int skippedMissing = 0;          // resolved but file doesn't exist (stale index entry)
    int skippedUnresolved = 0;       // shell couldn't resolve a path at all
    int skippedDuplicate = 0;        // same absolute path seen more than once
};

// Assembles the drag path list from the resolved samples (selection order). Comparison is
// exact-string — the shell normalizes case/slashes upstream if it wants Windows-style dedup.
PathList assemblePathList(const std::vector<ResolvedSample>& resolved);

// --- OS hand-off ordering -----------------------------------------------------

// Whether an empty/unresolvable payload should hand off to the OS at all. This decides ONLY
// the empty-payload third of the failure space — an unresolvable payload must leave the
// internal drag live rather than winding it down (release capture, clear drag state) for a
// hand-off that then never happens, which reads to the user as "the drag did nothing, try
// again". A resolved-but-OS-not-ready hand-off (OLE unavailable, HDROP build failure) is a
// separate, shell-side readiness gate (drag_out_win::canInitiateDragOut) checked BEFORE the
// shell tears down internal drag state — this struct does not model that path.
struct OsHandoff {
    bool handOffToOs = false;  // true: wind down internal drag state, then start the OS drag
};

// Decides the hand-off from the assembled path list. Empty (everything stale/unresolvable)
// means the internal drag stays live rather than dying half-torn-down.
OsHandoff decideOsHandoff(const std::vector<std::string>& paths);

} // namespace reasampler::ui
