#pragma once
// capture_realtime — the REAPER-free logic behind the realtime-record backend.
// The shell drives the transport, temp track, send routing, and file move; the
// pure pieces split out here and unit-tested outside the DAW are: (1) record-
// mode bookkeeping — scope + FX-tap point -> I_RECMODE/I_RECMODE_FLAGS values
// (bit MEANINGS transcribed verbatim from reaper_plugin_functions.h ~2197-2198;
// the CHOICE of value per scope is this module's tested logic) — and (2) the
// recorded-file -> Sample mapping (mirrors OfflineRenderBackend's population).

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

#include "core/model/bank_model.h"  // Sample, SourceMode (pure)

namespace reasampler::capture {

using model::Sample;
using model::Tier;
using model::SourceMode;

// I_RECMODE (verbatim from SDK header ~2197): 0=input, 1=stereo out, 2=none,
// 3=stereo out w/latency comp, 4=midi output, 5=mono out, 6=mono out w/latency
// comp, 7=midi overdub, 8=midi replace. We record a track's OUTPUT, latency-
// compensated, so the recorded file lines up sample-accurately with the source.
inline constexpr int kRecModeStereoOutLatComp = 3;  // stereo out w/latency comp
inline constexpr int kRecModeMonoOutLatComp   = 6;  // mono out w/latency comp

// I_RECMODE_FLAGS (verbatim from SDK header ~2198): &3=output recording mode
// (0=post fader, 1=pre-fx, 2=post-fx/pre-fader). This is the only documented
// pre-FX tap in the SDK — offline render has no pre-FX bit — so the realtime
// backend is the true pre-FX "dry" path.
inline constexpr int kRecOutPostFader       = 0;  // &3==0: post-fader (fully wet)
inline constexpr int kRecOutPreFx           = 1;  // &3==1: pre-FX (true dry)
inline constexpr int kRecOutPostFxPreFader  = 2;  // &3==2: post-FX, pre-fader

// The tap point on the source track's output the temp track records from.
// Orthogonal to the record mode (stereo/mono); this only sets the &3 flags bits.
enum class OutputTap {
    PostFader,       // fully wet, after this track's fader (kRecOutPostFader)
    PreFx,           // true dry, before this track's FX     (kRecOutPreFx)
    PostFxPreFader,  // wet FX, before the fader              (kRecOutPostFxPreFader)
};

// The concrete record-mode values a temp track must carry to capture the scoped
// output. `recMode` sets I_RECMODE (stereo/mono, latency-compensated);
// `recModeFlags` sets the &3 output-recording tap bits (we only own those bits).
struct RecordModePlan {
    int recMode      = kRecModeStereoOutLatComp;
    int recModeFlags = kRecOutPostFader;
};

// Maps (channelCount, tap) to the record-mode values: channelCount <= 1 ->
// mono-out latency-comp, else stereo-out; tap -> the &3 bits. The shell applies
// these via SetMediaTrackInfo_Value(I_RECMODE / I_RECMODE_FLAGS).
RecordModePlan recordModePlanFor(int channelCount, OutputTap tap);

// Maps a wetDry value to the output tap point: 1.0 (fully wet) -> PostFader,
// anything less -> PreFx (true dry — the realtime backend's distinguishing
// capability over offline render). PostFxPreFader is not reachable from wetDry.
OutputTap outputTapForWetDry(double wetDry);

// --- Recorded-file -> Sample mapping ----------------------------------------

// The inputs a finished realtime capture yields, gathered by the shell into a
// pure struct so Sample population is a single tested transform (mirrors the
// inline population in OfflineRenderBackend::capture).
struct RecordedCapture {
    // Project-relative path of the recorded file (the shell resolves REAPER's
    // absolute path back to project-relative).
    std::string relativePath;

    // The disambiguating tag that named the file (feeds the Sample id).
    std::string uniqueTag;

    // Echoed from the request (exact bounds — no re-measuring the file).
    SourceMode  sourceMode   = SourceMode::Realtime;
    double      startSeconds = 0.0;
    double      endSeconds   = 0.0;
    double      wetDry       = 1.0;
    std::string displayName;
    std::vector<std::string> trackGuids;

    int    channelCount = 0;

    // Left at defaults here — capture_realtime_finalize.cpp calls
    // stampCaptureSample(result.sample, ...) afterward, overwriting these five
    // from the live project. Kept because the pure unit tests still assert them.
    int    sampleRate   = 0;   // 0 when the project rate was unknown (as offline)
    double captureTempo = 0.0; // BPM at capture time
    int    captureTimeSigNum   = 0;  // 0/0 = unstamped
    int    captureTimeSigDenom = 0;
    std::int64_t createdTimestamp = 0;  // unix epoch seconds
};

// Builds the Sample for a finished realtime capture: exact request bounds,
// scratch tier, empty content hash, lengthSeconds = end - start. PPQ/beats
// left 0 (deferred, as offline).
Sample sampleFromRecordedCapture(const RecordedCapture& cap);

// --- Async record-phase state machine ----------------------------------------
//
// A realtime record spans many timer ticks (CSurf_OnRecord starts the transport
// on REAPER's audio thread and returns immediately — it does not block until the
// range completes). The completion decision — keep waiting, stop-and-flush,
// finalize, or give up — is pure and unit-tested without a DAW; the shell only
// reads the transport/clock/file and applies the verdict.
//
// Two waits, not one:
//   1. RECORD wait (Recording): transport running; wait for the play cursor to
//      reach the range end, OR the user stops early, OR a wall-clock safety
//      ceiling trips (a started-but-never-advancing transport).
//   2. FLUSH wait (Finalizing): transport stopped but REAPER closes/flushes the
//      recorded take on the audio thread — the file may lag a tick or two.
//      Defer the move until the file exists AND is stable, bounded by a flush
//      ceiling so a file that never appears fails cleanly instead of hanging.

// Where an in-progress capture is in its lifecycle: Recording (live, transport
// running) and Finalizing (live-but-stopped, waiting for flush) are the two
// waits above; Done/Failed are terminal — the shell's verdict to act on.
enum class RecordPhase {
    Recording,
    Finalizing,
    Done,
    Failed
};

// A distilled transport reading so the state machine never touches a REAPER
// type. `recording` is (GetPlayStateEx & 4) != 0; `playPosition` is
// GetPlayPositionEx (latency-compensated).
struct TransportReading {
    bool   recording    = false;
    double playPosition = 0.0;
};

// Everything the pure transition needs beyond the current phase, gathered by
// the shell each tick (the shell only reads and reports; never decides).
struct RecordTickInputs {
    TransportReading transport;
    double elapsedSeconds = 0.0;     // wall-clock since begin() — record ceiling
    double finalizingSeconds = 0.0;  // wall-clock in Finalizing — flush ceiling
    bool fileReady = false;          // recorded file exists+stable (Finalizing only)
};

// Record ceiling margin added to nominal duration: generous enough that
// pre-roll/count-in/latency never trips it, tight enough a stuck transport is
// force-terminated within seconds.
inline constexpr double kRecordMarginSeconds = 5.0;

// Max wall-clock Finalizing waits for the file to flush/stabilize before
// giving up (REAPER closes the take within a tick or two in practice).
inline constexpr double kFinalizeFlushCeilingSeconds = 5.0;

// The pure transition (total + deterministic). Done/Failed are sticky — a late
// tick before teardown finishes cannot flip the verdict (the idempotence the
// shell's single-restore relies on).
RecordPhase advanceRecordPhase(RecordPhase current,
                               const RecordTickInputs& inputs,
                               double rangeStartSeconds,
                               double rangeEndSeconds);

// True once the shell must STOP the transport and begin the flush wait — i.e. the
// phase has left Recording (Finalizing/Done/Failed). Used by the shell to fire the
// (idempotent) transport stop exactly on the Recording -> Finalizing edge.
bool isStopRequested(RecordPhase phase);

// True for the phases the shell must ACT on to conclude (finalize-or-fail + restore).
// Only Done and Failed are terminal; Recording and Finalizing are live.
bool isTerminalPhase(RecordPhase phase);

} // namespace reasampler::capture
