// meter_ballistics.cpp — see meter_ballistics.h.

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

#include <cmath>

namespace reasampler::instrument::engine {

double meterDbFromLinear(double linear) {
    if (!(linear > 0.0)) return kMeterFloorDb;  // also catches NaN
    const double db = 20.0 * std::log10(linear);
    return db < kMeterFloorDb ? kMeterFloorDb : db;
}

double meterNormFromDb(double db) {
    if (!(db > kMeterFloorDb)) return 0.0;  // also catches NaN
    if (db >= kMeterTopDb) return 1.0;
    return (db - kMeterFloorDb) / (kMeterTopDb - kMeterFloorDb);
}

MeterState advanceMeter(MeterState prev, double blockPeakLinear, double elapsedSeconds) {
    const double dt = (elapsedSeconds > 0.0) ? elapsedSeconds : 0.0;
    const double fall = kMeterFallDbPerSecond * dt;
    const double peakDb = meterDbFromLinear(blockPeakLinear);

    MeterState next = prev;
    // Instantaneous rise, timed fall — one expression, because a fall can never take the bar
    // below the peak this very block carried.
    const double fallen = prev.levelDb - fall;
    next.levelDb = fallen > peakDb ? fallen : peakDb;

    if (next.levelDb >= next.holdDb) {
        next.holdDb = next.levelDb;
        next.holdRemainingSeconds = kMeterPeakHoldSeconds;
    } else {
        next.holdRemainingSeconds = prev.holdRemainingSeconds - dt;
        if (next.holdRemainingSeconds < 0.0) {
            // Spend the overshoot as fall time so the tick's release does not quantize to the
            // UI frame it happened to expire on.
            const double held = kMeterFallDbPerSecond * -next.holdRemainingSeconds;
            const double dropped = next.holdDb - held;
            next.holdDb = dropped > next.levelDb ? dropped : next.levelDb;
            next.holdRemainingSeconds = 0.0;
        }
    }

    if (blockPeakLinear >= 1.0) next.clip = true;
    return next;
}

MeterState clearMeterClip(MeterState prev) {
    MeterState next = prev;
    next.clip = false;
    return next;
}

}  // namespace reasampler::instrument::engine
