#pragma once

#include <juce_audio_processors/juce_audio_processors.h>

#include <atomic>
#include <type_traits>

namespace deeppluck
{
namespace ParameterIDs
{
inline constexpr auto outputGain = "outputGain";
inline constexpr auto masterTune = "masterTune";
inline constexpr auto ampAttack = "ampAttack";
inline constexpr auto ampDecay = "ampDecay";
inline constexpr auto ampSustain = "ampSustain";
inline constexpr auto ampRelease = "ampRelease";
inline constexpr auto filterEnabled = "filterEnabled";
inline constexpr auto filterCutoff = "filterCutoff";
inline constexpr auto filterResonance = "filterResonance";
inline constexpr auto filterDrive = "filterDrive";
inline constexpr auto filterKeyTracking = "filterKeyTracking";
inline constexpr auto filterEnvAmount = "filterEnvAmount";
inline constexpr auto filterAttack = "filterAttack";
inline constexpr auto filterDecay = "filterDecay";
inline constexpr auto filterSustain = "filterSustain";
inline constexpr auto filterRelease = "filterRelease";
inline constexpr auto transientEnabled = "transientEnabled";
inline constexpr auto transientLevel = "transientLevel";
inline constexpr auto transientDecay = "transientDecay";
inline constexpr auto transientTone = "transientTone";
inline constexpr auto noiseEnabled = "noiseEnabled";
inline constexpr auto noiseLevel = "noiseLevel";
inline constexpr auto noiseDecay = "noiseDecay";
inline constexpr auto noiseTone = "noiseTone";
inline constexpr auto osc2Enabled = "osc2Enabled";
inline constexpr auto osc2Level = "osc2Level";
inline constexpr auto osc2Octave = "osc2Octave";
inline constexpr auto osc2Semitone = "osc2Semitone";
inline constexpr auto osc2FineTune = "osc2FineTune";
inline constexpr auto unisonVoices = "unisonVoices";
inline constexpr auto unisonDetune = "unisonDetune";
inline constexpr auto stereoSpread = "stereoSpread";
inline constexpr auto lfoRate = "lfoRate";
inline constexpr auto lfoSync = "lfoSync";
inline constexpr auto lfoSyncDivision = "lfoSyncDivision";
inline constexpr auto lfoWaveform = "lfoWaveform";
inline constexpr auto lfoRestartMode = "lfoRestartMode";
inline constexpr auto lfoFilterAmount = "lfoFilterAmount";
inline constexpr auto lfoPitchAmount = "lfoPitchAmount";
inline constexpr auto lfoSpreadAmount = "lfoSpreadAmount";
inline constexpr auto osc1Waveform = "osc1Waveform";
inline constexpr auto osc1PulseWidth = "osc1PulseWidth";
inline constexpr auto osc2Waveform = "osc2Waveform";
inline constexpr auto osc2PulseWidth = "osc2PulseWidth";
inline constexpr auto chorusBypass = "chorusBypass";
inline constexpr auto chorusRate = "chorusRate";
inline constexpr auto chorusDepth = "chorusDepth";
inline constexpr auto chorusWidth = "chorusWidth";
inline constexpr auto chorusMix = "chorusMix";
inline constexpr auto delayBypass = "delayBypass";
inline constexpr auto delaySync = "delaySync";
inline constexpr auto delayTime = "delayTime";
inline constexpr auto delaySyncDivision = "delaySyncDivision";
inline constexpr auto delayFeedback = "delayFeedback";
inline constexpr auto delayTone = "delayTone";
inline constexpr auto delayMix = "delayMix";
inline constexpr auto reverbBypass = "reverbBypass";
inline constexpr auto reverbSize = "reverbSize";
inline constexpr auto reverbPreDelay = "reverbPreDelay";
inline constexpr auto reverbDamping = "reverbDamping";
inline constexpr auto reverbMix = "reverbMix";
inline constexpr auto fxOrder = "fxOrder";
}

struct DeepPluckParameterSnapshot
{
    float outputGainDb = -3.0f;
    float masterTuneHz = 440.0f;
    float ampAttackSeconds = 0.003f;
    float ampDecaySeconds = 0.320f;
    float ampSustainLevel = 0.0f;
    float ampReleaseSeconds = 0.180f;
    float filterEnabled = 1.0f;
    float filterCutoffHz = 1800.0f;
    float filterResonance = 0.18f;
    float filterDriveDb = 3.0f;
    float filterKeyTracking = 0.35f;
    float filterEnvAmountOctaves = 2.50f;
    float filterAttackSeconds = 0.002f;
    float filterDecaySeconds = 0.280f;
    float filterSustainLevel = 0.0f;
    float filterReleaseSeconds = 0.180f;
    float transientEnabled = 1.0f;
    float transientLevelDb = -18.0f;
    float transientDecaySeconds = 0.012f;
    float transientTone = 0.55f;
    float noiseEnabled = 1.0f;
    float noiseLevelDb = -27.0f;
    float noiseDecaySeconds = 0.120f;
    float noiseTone = 0.35f;
    float osc2Enabled = 1.0f;
    float osc2LevelDb = -15.0f;
    float osc2Octave = 0.0f;
    float osc2Semitone = 0.0f;
    float osc2FineTuneCents = -7.0f;
    float unisonVoices = 2.0f;
    float unisonDetuneCents = 4.0f;
    float stereoSpread = 0.35f;
    float lfoRateHz = 0.25f;
    float lfoSync = 0.0f;
    float lfoSyncDivision = 4.0f;
    float lfoWaveform = 0.0f;
    float lfoRestartMode = 1.0f;
    float lfoFilterAmountOctaves = 0.0f;
    float lfoPitchAmountCents = 0.0f;
    float lfoSpreadAmount = 0.0f;
    float osc1Waveform = 0.0f;
    float osc1PulseWidth = 0.50f;
    float osc2Waveform = 0.0f;
    float osc2PulseWidth = 0.50f;
    float chorusBypass = 1.0f;
    float chorusRateHz = 0.35f;
    float chorusDepth = 0.25f;
    float chorusWidth = 0.65f;
    float chorusMix = 0.20f;
    float delayBypass = 1.0f;
    float delaySync = 1.0f;
    float delayTimeSeconds = 0.375f;
    float delaySyncDivision = 1.0f;
    float delayFeedback = 0.32f;
    float delayToneHz = 6000.0f;
    float delayMix = 0.22f;
    float reverbBypass = 1.0f;
    float reverbSize = 0.55f;
    float reverbPreDelaySeconds = 0.018f;
    float reverbDamping = 0.55f;
    float reverbMix = 0.18f;
    float fxOrder = 0.0f;
};

static_assert (std::is_trivially_copyable_v<DeepPluckParameterSnapshot>);

juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
[[nodiscard]] juce::String formatParameterValue (const juce::String& parameterID,
                                                  float rawValue,
                                                  int maximumLength = 0);
[[nodiscard]] float parseParameterValue (const juce::String& parameterID,
                                         const juce::String& text) noexcept;
juce::ValueTree mergeRestoredState (const juce::ValueTree& currentState,
                                    const juce::ValueTree& restoredState);

class DeepPluckParameterCache final
{
public:
    explicit DeepPluckParameterCache (juce::AudioProcessorValueTreeState& state) noexcept;

    [[nodiscard]] bool isValid() const noexcept;
    [[nodiscard]] DeepPluckParameterSnapshot getSnapshot() const noexcept;

private:
    std::atomic<float>* outputGain = nullptr;
    std::atomic<float>* masterTune = nullptr;
    std::atomic<float>* ampAttack = nullptr;
    std::atomic<float>* ampDecay = nullptr;
    std::atomic<float>* ampSustain = nullptr;
    std::atomic<float>* ampRelease = nullptr;
    std::atomic<float>* filterEnabled = nullptr;
    std::atomic<float>* filterCutoff = nullptr;
    std::atomic<float>* filterResonance = nullptr;
    std::atomic<float>* filterDrive = nullptr;
    std::atomic<float>* filterKeyTracking = nullptr;
    std::atomic<float>* filterEnvAmount = nullptr;
    std::atomic<float>* filterAttack = nullptr;
    std::atomic<float>* filterDecay = nullptr;
    std::atomic<float>* filterSustain = nullptr;
    std::atomic<float>* filterRelease = nullptr;
    std::atomic<float>* transientEnabled = nullptr;
    std::atomic<float>* transientLevel = nullptr;
    std::atomic<float>* transientDecay = nullptr;
    std::atomic<float>* transientTone = nullptr;
    std::atomic<float>* noiseEnabled = nullptr;
    std::atomic<float>* noiseLevel = nullptr;
    std::atomic<float>* noiseDecay = nullptr;
    std::atomic<float>* noiseTone = nullptr;
    std::atomic<float>* osc2Enabled = nullptr;
    std::atomic<float>* osc2Level = nullptr;
    std::atomic<float>* osc2Octave = nullptr;
    std::atomic<float>* osc2Semitone = nullptr;
    std::atomic<float>* osc2FineTune = nullptr;
    std::atomic<float>* unisonVoices = nullptr;
    std::atomic<float>* unisonDetune = nullptr;
    std::atomic<float>* stereoSpread = nullptr;
    std::atomic<float>* lfoRate = nullptr;
    std::atomic<float>* lfoSync = nullptr;
    std::atomic<float>* lfoSyncDivision = nullptr;
    std::atomic<float>* lfoWaveform = nullptr;
    std::atomic<float>* lfoRestartMode = nullptr;
    std::atomic<float>* lfoFilterAmount = nullptr;
    std::atomic<float>* lfoPitchAmount = nullptr;
    std::atomic<float>* lfoSpreadAmount = nullptr;
    std::atomic<float>* osc1Waveform = nullptr;
    std::atomic<float>* osc1PulseWidth = nullptr;
    std::atomic<float>* osc2Waveform = nullptr;
    std::atomic<float>* osc2PulseWidth = nullptr;
    std::atomic<float>* chorusBypass = nullptr;
    std::atomic<float>* chorusRate = nullptr;
    std::atomic<float>* chorusDepth = nullptr;
    std::atomic<float>* chorusWidth = nullptr;
    std::atomic<float>* chorusMix = nullptr;
    std::atomic<float>* delayBypass = nullptr;
    std::atomic<float>* delaySync = nullptr;
    std::atomic<float>* delayTime = nullptr;
    std::atomic<float>* delaySyncDivision = nullptr;
    std::atomic<float>* delayFeedback = nullptr;
    std::atomic<float>* delayTone = nullptr;
    std::atomic<float>* delayMix = nullptr;
    std::atomic<float>* reverbBypass = nullptr;
    std::atomic<float>* reverbSize = nullptr;
    std::atomic<float>* reverbPreDelay = nullptr;
    std::atomic<float>* reverbDamping = nullptr;
    std::atomic<float>* reverbMix = nullptr;
    std::atomic<float>* fxOrder = nullptr;
};
}
