// meter_ballistics.h — the output meter's ballistics and its dB scale: peak fall, peak hold,
// clip latch, and the dB -> normalized map the bar draws against. UI-thread math ONLY: the
// audio thread publishes raw block peaks per block and converts, holds and decays nothing.

#pragma once

namespace reasampler::instrument::engine {

// The scale is LINEAR IN dB across this span. Above 0 dBFS is shown because that is exactly
// what the limiter-off case has to make visible.
inline constexpr double kMeterFloorDb = -60.0;
inline constexpr double kMeterTopDb = 6.0;

// A peak meter must not smooth its attack or it under-reports, so the rise is instantaneous
// and only the fall is timed. 20 dB/s is close to the IEC 60268-18 PPM fallback.
inline constexpr double kMeterFallDbPerSecond = 20.0;
inline constexpr double kMeterPeakHoldSeconds = 1.5;

// Linear magnitude -> dBFS, floored at kMeterFloorDb — a silent block reads the floor rather
// than -inf, so the state stays a finite number the ballistics can subtract from.
double meterDbFromLinear(double linear);

// dBFS -> [0,1] up the meter, clamped at both ends.
double meterNormFromDb(double db);

struct MeterState {
    double levelDb = kMeterFloorDb;
    double holdDb = kMeterFloorDb;
    double holdRemainingSeconds = 0.0;
    bool clip = false;  // latched; only clearMeterClip lowers it
};

// One UI frame of ballistics against the block peak the audio thread published and the time
// since the previous frame. Clip latches at a block peak >= 0 dBFS and is never cleared here.
MeterState advanceMeter(MeterState prev, double blockPeakLinear, double elapsedSeconds);

// The click-to-clear on the meter's clip cap.
MeterState clearMeterClip(MeterState prev);

}  // namespace reasampler::instrument::engine
