// meter_accumulate.h — the master meter's ACCUMULATE half: the audio thread's block-rate fold
// into the two windows the UI drains, and the drain that starts the next window. The ballistics
// that run on what comes out are meter_ballistics'. Header-only — the folds sit on the audio
// thread's per-block path. The folds are templated on the accumulator ONLY so the
// drain-inside-the-fold interleave below can be pinned deterministically instead of raced for.

#pragma once

#include <atomic>

namespace reasampler::instrument::engine {

// A lock-backed std::atomic<float> would put a mutex on the audio thread; assert the freedom
// rather than assume it.
static_assert(std::atomic<float>::is_always_lock_free,
              "the meter folds run on the audio thread and must be lock-free");

// The two windows' identity elements: a peak window that has seen nothing reports silence, a
// gain window that has seen nothing reports no reduction. They are what a consume reinstalls,
// so they live beside the folds rather than at the reader.
inline constexpr float kMeterPeakIdentity = 0.f;
inline constexpr float kMeterGainIdentity = 1.f;

// Folds one block's reading into its accumulator — a running max for a peak, a running min for
// the limiter's gain — so the ~47 blocks that elapse between two 500 ms UI frames at 48 kHz/512
// all reach the meter instead of the one it happened to sample.
//
// An UNCONDITIONAL read-modify-write, and that is the whole point. The UI's consume is an
// exchange that can land between a plain load and its store, and a load-compare-store fold
// would then drop the block outright: it decided against storing by comparing with a window the
// UI has since taken, so that block's reading enters neither the old window nor the new one.
// The CAS retries against whatever the consume left, which makes `acc >= blockPeak` hold on
// exit however the two interleave. STRONG, so the loop is bounded by the interference it is
// written against: the audio thread is the only writer besides the UI's single consume, and
// weak's permitted spurious failure would make an unbounded retry count reachable with no
// interference at all. Three calls per block, so the strong form costs nothing measurable.
// Relaxed throughout: the accumulators are advisory and order no other state. `Accumulator` is
// templated only so a test can pin the interleave; it must behave as std::atomic<float>.
template <class Accumulator>
inline void foldPeak(Accumulator& acc, float blockPeak) {
    float seen = acc.load(std::memory_order_relaxed);
    while (!acc.compare_exchange_strong(seen, seen > blockPeak ? seen : blockPeak,
                                        std::memory_order_relaxed,
                                        std::memory_order_relaxed)) {
    }
}

template <class Accumulator>
inline void foldMinGain(Accumulator& acc, float blockMinGain) {
    float seen = acc.load(std::memory_order_relaxed);
    while (!acc.compare_exchange_strong(seen, seen < blockMinGain ? seen : blockMinGain,
                                        std::memory_order_relaxed,
                                        std::memory_order_relaxed)) {
    }
}

// Takes what the window accumulated and reinstalls the identity element, which IS what starts
// the next window — so exactly one reader may consume (the shell's MasterBusMeter states who).
// Concrete: only the folds have the interleave a test seam buys, and a template over one
// instantiation models nothing.
inline float consumePeak(std::atomic<float>& acc) {
    return acc.exchange(kMeterPeakIdentity, std::memory_order_relaxed);
}

inline float consumeMinGain(std::atomic<float>& acc) {
    return acc.exchange(kMeterGainIdentity, std::memory_order_relaxed);
}

}  // namespace reasampler::instrument::engine
