#pragma once
// instrument_drop — pure payload-construction core of drop-and-load: dropping a
// bank capture onto a track's panel or FX surface instantiates ReaSampler 9000 on
// that track already playing that capture. Also the classifier that names those
// surfaces. No REAPER/SWELL/VST3 SDK/vendor includes
// (+ the pure sample_map it reuses and the SDK-free UID macros in
// reasampler_uid.h); unit-tested outside the DAW.
//
// Mechanism: after TrackFX_AddByName creates the instance, the extension
// writes a Steinberg-format .vstpreset file whose 'Comp' chunk is the
// instrument's own component state (capture pre-selected) and applies it via
// TrackFX_SetPreset — the SDK-documented path for VST3 plug-ins.
//
// NOT TrackFX_SetNamedConfigParm("vst_chunk", ...): for VST3 that string is
// REAPER's own wrapper framing, not the raw IComponent::setState stream —
// writing raw component-state bytes there "succeeds" but the wrapper cannot
// apply the unframed blob, leaving the instance silently at defaults. The
// .vstpreset container is Steinberg-documented (public.sdk/source/vst/
// vstpresetfile.cpp is the reference layout) and buildable byte-exactly.
//
// The component-state bytes are produced by the instrument's own serializer
// (sample_map::serializeComponentState, the same function getState calls),
// so this module cannot drift from the instrument's format.

#include <cstdint>
#include <string>
#include <vector>

#include "core/ui/drag_out.h"  // ReaperSurface — the surface vocabulary the gesture law reads

namespace reasampler::wire {

// The 32-char uppercase-hex class-ID string of this build's channel-active
// ReaSampler 9000 VST3 class UID — exactly what Steinberg::FUID::toString
// renders and what a .vstpreset header carries (the four INLINE_UID uint32
// words printed "%08X" in order, platform-stable on both COM-compatible and
// plain layouts). Sourced from reasampler_uid.h, channel-selected — a beta
// extension writes presets only the beta VST class accepts.
std::string vstClassIdHex();

// Builds a Steinberg VST3 preset image with exactly one 'Comp' chunk =
// `componentState`, addressed to class `classIdHex32` (32 hex chars). Layout
// per public.sdk/source/vst/vstpresetfile.cpp, little-endian:
//   [0] 'VST3' [4] int32 version=1 [8] 32-char class ID
//   [40] int64 chunk-list offset (= 48 + componentState.size())
//   [48] component-state bytes, then 'List' + entry count=1 + {'Comp', 48, size}.
// No 'Cont' chunk (a SingleComponentEffect's controller state is optional in
// the container format). Empty vector when classIdHex32 isn't 32 chars.
std::vector<std::uint8_t> buildVstPresetBytes(const std::string& classIdHex32,
                                              const std::vector<std::uint8_t>& componentState);

// The drop payload: a .vstpreset for the channel-active class with just
// `sampleId` picked (no zones, mono, generation 0) — what a fresh instance
// would hold after a browser click. Empty sampleId yields the empty-state
// preset. Deterministic.
std::vector<std::uint8_t> buildInstrumentDropPreset(const std::string& sampleId);

// Pure classifier for GetThingFromPoint's (info string, track-was-returned) pair
// into the surfaces the drag-out gesture law distinguishes. The SDK warns future
// versions may append information, so every rule is PREFIX-based:
//   "tcp.fxembed*" / "mcp.fxembed*" -> FxEmbed. Checked FIRST: it is the surface
//       an existing instance already draws on, and a drop must not stack a second.
//   "fx_*"                          -> FxSurface (FX chain + floating FX windows).
//   "tcp*" / "mcp*"                 -> TrackPanel — the WHOLE panel, every
//       sub-element, not just the FX button. A TCP too narrow to draw that button
//       still means "sampler on this track", and the old glyph-only rule is exactly
//       why such a drop landed nowhere.
//   "arrange*"                      -> Arrange.
//   ""  with no track               -> OffReaper (the pointer left REAPER).
//   anything else, incl. "" WITH a track -> Other. Over REAPER on a surface we
//       cannot name: refuse visibly, never guess an outcome.
// The exact live token is DAW-only — confirm via
// reaper.GetThingFromPoint(reaper.GetMousePosition()) in ReaScript if unsure.
ui::ReaperSurface classifyReaperSurface(const std::string& info, bool haveTrack);

// The raw component-state bytes the preset carries, exposed so the round-trip
// test can decode them back through sample_map::deserializeComponentState and
// assert the capture is selected. Not called by the shell.
std::vector<std::uint8_t> instrumentDropStateBytes(const std::string& sampleId);

// --- All-or-nothing rollback --------------------------------------------------

// What the shell observed while executing one drop, reduced to the two REAPER
// results the contract turns on.
struct DropAttempt {
    int  addedFxIndex = -1;    // TrackFX_AddByName's return; < 0 = nothing was created
    bool presetApplied = false;  // TrackFX_SetPreset's return
};

// The verdict. `rollbackFxIndex >= 0` obliges the caller to TrackFX_Delete it before
// returning — an instance whose capture never landed must not survive the drop.
struct DropOutcome {
    bool loaded = false;
    int  rollbackFxIndex = -1;
};

// Pure so the contract is provable without a DAW: the rollback obligation is decided
// here, not inline in the shell, and holds identically for every drop surface (FX
// button, FX chain/container, Media-Explorer import).
DropOutcome decideDropOutcome(const DropAttempt& attempt);

}  // namespace reasampler::wire
