// live_params.cpp — the fold from the parameter set to the live block, and the ramp-step law.
// See live_params.h for the publication contract.

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

namespace reasampler::instrument::engine {

LiveValues foldLive(const PlayParams& params, double keyTrack) {
    // Value-initialized, so the padding is determinate too. Nothing reads it — the block's
    // equality is field-wise for exactly that reason — but this is the one construction site
    // every publisher goes through, and an object with indeterminate bytes travelling under a
    // seqlock is a hazard worth not having. Off the audio thread; the memset costs nothing here.
    LiveValues v{};
    v.keyTrack = keyTrack;
    // Folded here, not at the voice: Voice::start reads the block's value directly, so the
    // spline rule has to be applied on the way in or the two would answer differently.
    v.splineActive = splineActive(params);
    v.lengthFraction = effectiveLengthFraction(params);
    v.filterSettings = params.filter.settings;
    v.filterModAmount = params.filter.modAmount;
    v.filterVelAmount = params.filter.velAmount;
    v.filterKeyTrack = params.filter.keyTrack;
    v.filterEnv = params.filter.env;
    v.filterAhd = params.filter.trigEnv;
    v.adsr = params.adsr;
    v.ampAhd = params.trigAhd;
    v.pitchEnv = params.pitchEnv;
    v.playRate = params.playRate;
    v.pitchOffsetSemitones = params.pitchOffsetSemitones;
    return v;
}

namespace {

bool sameAdsr(const AdsrParams& a, const AdsrParams& b) {
    return a.attackFrames == b.attackFrames && a.holdFrames == b.holdFrames &&
           a.decayFrames == b.decayFrames && a.sustainLevel == b.sustainLevel &&
           a.releaseFrames == b.releaseFrames && a.attackCurve == b.attackCurve &&
           a.decayCurve == b.decayCurve && a.releaseCurve == b.releaseCurve;
}

bool sameAhd(const AhdParams& a, const AhdParams& b) {
    return a.attackFrames == b.attackFrames && a.decayFrames == b.decayFrames &&
           a.holdFraction == b.holdFraction && a.attackCurve == b.attackCurve &&
           a.decayCurve == b.decayCurve;
}

bool sameFilterSettings(const filter::FilterSettings& a, const filter::FilterSettings& b) {
    return a.cutoffNorm == b.cutoffNorm && a.resonanceNorm == b.resonanceNorm &&
           a.morphNorm == b.morphNorm && a.driveNorm == b.driveNorm &&
           a.morphLaw == b.morphLaw;
}

}  // namespace

bool operator==(const LiveValues& a, const LiveValues& b) {
    return sameFilterSettings(a.filterSettings, b.filterSettings) &&
           a.filterModAmount == b.filterModAmount &&
           a.filterVelAmount == b.filterVelAmount &&
           a.filterKeyTrack == b.filterKeyTrack &&
           sameAdsr(a.filterEnv, b.filterEnv) &&
           sameAhd(a.filterAhd, b.filterAhd) &&
           sameAdsr(a.adsr, b.adsr) &&
           sameAhd(a.ampAhd, b.ampAhd) &&
           a.pitchEnv.enabled == b.pitchEnv.enabled &&
           a.pitchEnv.peakSemitones == b.pitchEnv.peakSemitones &&
           sameAhd(a.pitchEnv.shape, b.pitchEnv.shape) &&
           a.playRate == b.playRate &&
           a.pitchOffsetSemitones == b.pitchOffsetSemitones &&
           a.keyTrack == b.keyTrack &&
           a.lengthFraction == b.lengthFraction &&
           a.splineActive == b.splineActive;
}

double liveRampStep(double sampleRate) {
    if (!(sampleRate > 0.0)) return 0.0;  // also catches NaN
    return 1.0 / (kLiveRampSeconds * sampleRate);
}

}  // namespace reasampler::instrument::engine
