// processor_snapshot.h — the two namespace-scope aggregates the processor hands ACROSS its
// thread boundary: the loaded instrument the audio thread renders, and the bus state it
// publishes back for the editor's meter. Neither is behaviour, which is what makes them a
// responsibility rather than a bisection of the processor's own declaration.

#pragma once

#include <cstddef>
#include <cstdint>

#include "core/instrument/engine/voice_engine.h"

namespace reasampler::vst {

// What the audio thread publishes about the OUTPUT BUS, post-limiter, once per block. Raw
// magnitudes only — the UI converts to dB and runs the ballistics (engine/meter_ballistics),
// because a hold timer or a log on the audio thread would be per-block work that buys nothing.
struct MasterBusMeter {
    float peakL = 0.f;     // max |x| this block
    float peakR = 0.f;
    // Smallest gain the LIMITER computed this block (Limiter::process) — deliberately NOT
    // scaled by the transition mute, so a toggle over quiet material reads 1 (no reduction)
    // rather than the mute's own weight. 1 = no reduction.
    float minGain = 1.f;
    bool clip = false;     // LATCHED at a block peak >= 0 dBFS; only clearMasterBusClip lowers it
};

// The decoded capture + the voice engine playing it. The engine holds a reference to the
// sample, so both must live/die together at a stable address — heap-allocated,
// non-copyable, non-movable. process() only ever reads this through an atomic pointer.
struct LoadedInstrument {
    SampleData sample;
    VoiceEngine engine;
    std::uint64_t installedAt = 0;  // reloadGeneration_ at which this was installed into live_

    // Takeover declick is on by default here (product default; the pure core defaults it
    // off): any voice restart (mono retrigger, legato, poly steal, preview) ramps instead
    // of clicking.
    LoadedInstrument(SampleData sd, std::size_t maxVoices,
                     std::uint64_t gen, std::size_t preserveVoiceCap = 0,
                     std::int64_t preserveWindowFrames = 0,
                     VoiceMode voiceMode = VoiceMode::Poly,
                     MonoTrigger monoTrigger = MonoTrigger::Retrigger)
        : sample(std::move(sd)),
          engine(maxVoices, sample, preserveVoiceCap, preserveWindowFrames,
                 voiceMode, monoTrigger, /*takeoverDeclick=*/true),
          installedAt(gen) {}

    // True when nothing in this snapshot is sounding; lets the off-thread retirer park an
    // idle drain early. Bounded scan (<= maxVoices).
    bool fullyIdle() const { return engine.activeVoiceCount() == 0; }

    LoadedInstrument(const LoadedInstrument&) = delete;
    LoadedInstrument& operator=(const LoadedInstrument&) = delete;
};

}  // namespace reasampler::vst
