// capture_realtime.cpp — pure logic for the realtime-record backend. See header.
// Unit-tested by tests/test_capture_realtime.cpp.

#include "core/capture/capture_realtime.h"

namespace reasampler::capture {

RecordModePlan recordModePlanFor(int channelCount, OutputTap tap) {
    RecordModePlan p;

    // REAPER's output-record modes are mono/stereo only; >2 channels still
    // records stereo-out (a >2-channel realtime capture is out of scope).
    p.recMode = (channelCount <= 1) ? kRecModeMonoOutLatComp
                                    : kRecModeStereoOutLatComp;

    switch (tap) {
        case OutputTap::PostFader:      p.recModeFlags = kRecOutPostFader;      break;
        case OutputTap::PreFx:          p.recModeFlags = kRecOutPreFx;          break;
        case OutputTap::PostFxPreFader: p.recModeFlags = kRecOutPostFxPreFader; break;
    }
    return p;
}

OutputTap outputTapForWetDry(double wetDry) {
    return (wetDry >= 1.0) ? OutputTap::PostFader : OutputTap::PreFx;
}

Sample sampleFromRecordedCapture(const RecordedCapture& cap) {
    Sample s;
    // Relative path tail included so two same-tag captures (shouldn't happen) still differ.
    s.id           = "cap-" + cap.uniqueTag + "-" + cap.relativePath;
    s.displayName  = cap.displayName;
    s.relativePath = cap.relativePath;
    s.sourceMode   = cap.sourceMode;
    s.sourceRange.startSeconds = cap.startSeconds;
    s.sourceRange.endSeconds   = cap.endSeconds;
    // PPQ/beats deferred (musical-placement concern), as offline.
    s.wetDry        = cap.wetDry;
    s.trackGuids    = cap.trackGuids;
    s.channelCount  = cap.channelCount;
    s.sampleRate    = cap.sampleRate;   // 0 when project rate was unknown
    s.lengthSeconds = cap.endSeconds - cap.startSeconds;
    s.captureTempo  = cap.captureTempo;
    s.captureTimeSigNum   = cap.captureTimeSigNum;    // 0/0 = unstamped
    s.captureTimeSigDenom = cap.captureTimeSigDenom;
    s.tier          = Tier::Scratch;
    // contentHash is left empty: this mapping runs before the file exists on
    // disk; the shell patches the hash in after the move+trim.
    // rootNote/loop left empty: a realtime record of wet output isn't a single
    // played note, so no root note is derivable; loop points are a later action.
    s.createdTimestamp = cap.createdTimestamp;
    return s;
}

RecordPhase advanceRecordPhase(RecordPhase current,
                               const RecordTickInputs& inputs,
                               double rangeStartSeconds,
                               double rangeEndSeconds) {
    switch (current) {
        case RecordPhase::Recording: {
            // Stopped early (user or REAPER) -> finalize what was captured so far.
            if (!inputs.transport.recording) return RecordPhase::Finalizing;

            // >= (not >): a cursor landing exactly on the end completes.
            if (inputs.transport.playPosition >= rangeEndSeconds)
                return RecordPhase::Finalizing;

            // Self-defense: a stuck/looping transport that never reaches end would
            // otherwise stay in Recording forever, leaking the temp track + armed sink.
            const double ceiling =
                (rangeEndSeconds - rangeStartSeconds) + kRecordMarginSeconds;
            if (inputs.elapsedSeconds > ceiling) return RecordPhase::Finalizing;

            return RecordPhase::Recording;
        }

        case RecordPhase::Finalizing: {
            // Moving the file before it's stable would race REAPER's flush and
            // yield a truncated/missing capture.
            if (inputs.fileReady) return RecordPhase::Done;

            if (inputs.finalizingSeconds > kFinalizeFlushCeilingSeconds)
                return RecordPhase::Failed;

            return RecordPhase::Finalizing;
        }

        case RecordPhase::Done:
        case RecordPhase::Failed:
        default:
            return current;
    }
}

bool isStopRequested(RecordPhase phase) {
    return phase != RecordPhase::Recording;
}

bool isTerminalPhase(RecordPhase phase) {
    return phase == RecordPhase::Done || phase == RecordPhase::Failed;
}

} // namespace reasampler::capture
