#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_dsp/juce_dsp.h>
#include "DSP/Voice.h"
#include "DSP/Distortion.h"
#include "DSP/Compressor.h"
#include "DSP/Delay.h"
#include "DSP/Reverb.h"
#include "PresetManager.h"
#include <array>
#include <atomic>

class ChromaFlockProcessor : public juce::AudioProcessor {
public:
    ChromaFlockProcessor();
    ~ChromaFlockProcessor() override;

    void prepareToPlay(double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;
    void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;

    juce::AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override { return true; }

    const juce::String getName() const override { return "ChromaFlock"; }
    bool acceptsMidi() const override { return true; }
    bool producesMidi() const override { return false; }
    bool isMidiEffect() const override { return false; }
    double getTailLengthSeconds() const override { return 0.0; }

    int getNumPrograms() override { return 1; }
    int getCurrentProgram() override { return 0; }
    void setCurrentProgram(int) override {}
    const juce::String getProgramName(int) override { return {}; }
    void changeProgramName(int, const juce::String&) override {}

    void getStateInformation(juce::MemoryBlock& destData) override;
    void setStateInformation(const void* data, int sizeInBytes) override;

    juce::AudioProcessorValueTreeState apvts;
    PresetManager presetManager;

    static constexpr int scopeBufferSize = 1024;
    std::array<float, scopeBufferSize> scopeBuffer{};
    std::atomic<int> scopeWritePos{0};

    static constexpr int fftOrder = 11;
    static constexpr int fftSize = 1 << fftOrder;
    std::array<float, fftSize> fftInput{};
    std::atomic<int> fftWritePos{0};

    float getRmsLevel() const { return rmsLevel.load(); }

    void noteOn(int midiNote, float velocity) {
        if (midiNote >= 0 && midiNote < 128)
            activeNotes[midiNote].store(true, std::memory_order_relaxed);
        synth.noteOn(1, midiNote, velocity);
    }
    void noteOff(int midiNote) {
        if (midiNote >= 0 && midiNote < 128)
            activeNotes[midiNote].store(false, std::memory_order_relaxed);
        synth.noteOff(1, midiNote, 0.0f, true);
    }
    bool isNoteActive(int midiNote) const {
        return midiNote >= 0 && midiNote < 128
               && activeNotes[midiNote].load(std::memory_order_relaxed);
    }

    std::atomic<float> pitchBendValue{0.0f};
    float getPitchBend() const { return pitchBendValue.load(std::memory_order_relaxed); }

private:
    juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();

    void updateVoiceParameters();

    juce::Synthesiser synth;
    static constexpr int maxVoices = 16;

    Distortion distortion;
    Compressor compressor;
    Delay delay;
    ReverbFX reverbFX;
    float autoPanPhase = 0.0f;

    double currentSampleRate = 44100.0;

    std::atomic<float> rmsLevel{0.0f};
    std::array<std::atomic<bool>, 128> activeNotes{};

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockProcessor)
};
