// param_live.cpp — see param_live.h. ONE exhaustive routing switch and one shared value map;
// every law is called, none is restated.

#include "core/instrument/param/param_live.h"

#include <cstdint>

#include "core/instrument/map/play_seconds.h"   // secondsToFrames (resolvePlay's own fold)
#include "core/instrument/param/param_units.h"  // hostStoredFromNorm (the ONE host value map)
#include "core/instrument/ui/deck_values.h"     // the field resolvers setDeckParam writes through

namespace reasampler::instrument::param {

using engine::LiveValues;

bool applyLiveParam(LiveValues& block, DeckParam deck, double normalized, int sampleRate) {
    const double stored = hostStoredFromNorm(deck, normalized);
    const auto frames = [&](std::int64_t& dst) {
        dst = map::secondsToFrames(stored, static_cast<double>(sampleRate));
        return true;
    };
    const auto position = [&](float& dst) { dst = static_cast<float>(stored); return true; };
    const auto value = [&](double& dst) { dst = stored; return true; };

    // NO `default:` — see the header. A promotion that forgets this file is a compile error.
    switch (deck) {
        // The fourteen stage times: stored seconds resolved at the BUILT rate.
        case DeckParam::kAttack:                return frames(block.adsr.attackFrames);
        case DeckParam::kHold:                  return frames(block.adsr.holdFrames);
        case DeckParam::kDecay:                 return frames(block.adsr.decayFrames);
        case DeckParam::kRelease:               return frames(block.adsr.releaseFrames);
        case DeckParam::kTrigAttack:            return frames(block.ampAhd.attackFrames);
        case DeckParam::kTrigDecay:             return frames(block.ampAhd.decayFrames);
        case DeckParam::kPitchEnvAttack:        return frames(block.pitchEnv.shape.attackFrames);
        case DeckParam::kPitchEnvDecay:         return frames(block.pitchEnv.shape.decayFrames);
        case DeckParam::kFilterEnvAttack:       return frames(block.filterEnv.attackFrames);
        case DeckParam::kFilterEnvHold:         return frames(block.filterEnv.holdFrames);
        case DeckParam::kFilterEnvDecay:        return frames(block.filterEnv.decayFrames);
        case DeckParam::kFilterEnvRelease:      return frames(block.filterEnv.releaseFrames);
        case DeckParam::kFilterTrigAttack:      return frames(block.filterAhd.attackFrames);
        case DeckParam::kFilterTrigDecay:       return frames(block.filterAhd.decayFrames);

        // The filter's four, which store their normalized position as float exactly as the
        // parameter set stores it.
        case DeckParam::kFilterMorph:           return position(block.filterSettings.morphNorm);
        case DeckParam::kFilterCutoff:          return position(block.filterSettings.cutoffNorm);
        case DeckParam::kFilterQ:               return position(block.filterSettings.resonanceNorm);
        case DeckParam::kFilterDrive:           return position(block.filterSettings.driveNorm);

        // Everything the block carries verbatim as a double.
        case DeckParam::kSustain:               return value(block.adsr.sustainLevel);
        case DeckParam::kAttackCurve:           return value(block.adsr.attackCurve);
        case DeckParam::kDecayCurve:            return value(block.adsr.decayCurve);
        case DeckParam::kReleaseCurve:          return value(block.adsr.releaseCurve);
        case DeckParam::kTrigHold:              return value(block.ampAhd.holdFraction);
        case DeckParam::kTrigAttackCurve:       return value(block.ampAhd.attackCurve);
        case DeckParam::kTrigDecayCurve:        return value(block.ampAhd.decayCurve);
        case DeckParam::kPitchEnvHold:          return value(block.pitchEnv.shape.holdFraction);
        case DeckParam::kPitchEnvAttackCurve:   return value(block.pitchEnv.shape.attackCurve);
        case DeckParam::kPitchEnvDecayCurve:    return value(block.pitchEnv.shape.decayCurve);
        case DeckParam::kPitchEnvDepth:         return value(block.pitchEnv.peakSemitones);
        case DeckParam::kFilterEnvSustain:      return value(block.filterEnv.sustainLevel);
        case DeckParam::kFilterEnvAttackCurve:  return value(block.filterEnv.attackCurve);
        case DeckParam::kFilterEnvDecayCurve:   return value(block.filterEnv.decayCurve);
        case DeckParam::kFilterEnvReleaseCurve: return value(block.filterEnv.releaseCurve);
        case DeckParam::kFilterTrigHold:        return value(block.filterAhd.holdFraction);
        case DeckParam::kFilterTrigAttackCurve: return value(block.filterAhd.attackCurve);
        case DeckParam::kFilterTrigDecayCurve:  return value(block.filterAhd.decayCurve);
        case DeckParam::kFilterModAmt:          return value(block.filterModAmount);
        case DeckParam::kFilterVel:             return value(block.filterVelAmount);
        case DeckParam::kFilterKeyTrack:        return value(block.filterKeyTrack);
        case DeckParam::kRate:                  return value(block.playRate);
        case DeckParam::kPitch:                 return value(block.pitchOffsetSemitones);
        case DeckParam::kKeyTrack:              return value(block.keyTrack);

        // The one control the block does not carry verbatim: what it publishes is the
        // SPLINE-FOLDED fraction, so a write while a contour is active must be inert here for the
        // same reason the knob is inert in the editor.
        case DeckParam::kTrigLength:
            if (!block.splineActive) block.lengthFraction = stored;
            return true;

        // Not carried. Master gain reaches the audio beside the block, as the processor's own
        // atomic; the rest are toggles, radios, curve-popup cells and the deck's processor-side
        // controls — all Reload- or rebuild-tier, so none of them is an exposed parameter.
        case DeckParam::kMasterGain:
        case DeckParam::kPlayMode:
        case DeckParam::kPitchEngine:
        case DeckParam::kPitchEnvEnable:
        case DeckParam::kFilterEnable:
        case DeckParam::kFilterLaw:
        case DeckParam::kAmpVelCurve:
        case DeckParam::kPitchVelCurve:
        case DeckParam::kFilterVelCurve:
        case DeckParam::kAmpEnvSelect:
        case DeckParam::kPitchEnvSelect:
        case DeckParam::kFilterEnvSelect:
        case DeckParam::kAmpEnvMode:
        case DeckParam::kPitchEnvMode:
        case DeckParam::kFilterEnvMode:
        case DeckParam::kVoiceCount:
        case DeckParam::kVoiceMode:
        case DeckParam::kMonoTrigger:
        case DeckParam::kLimiterEnable:
        case DeckParam::kMasterMeter:
        case DeckParam::kMasterGr:
        case DeckParam::kCount:
            return false;
    }
    return false;  // unreachable for a valid enumerator; silences a warning.
}

bool writeHostParam(DeckParam deck, map::PlaySeconds& play, double normalized) {
    const double stored = hostStoredFromNorm(deck, normalized);
    if (float* f = ui::deckFloatField(deck, play)) {
        *f = static_cast<float>(stored);
        return true;
    }
    if (double* d = ui::deckDoubleField(deck, play)) {
        *d = stored;
        return true;
    }
    return false;
}

}  // namespace reasampler::instrument::param
