#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_formats/juce_audio_formats.h>
#include <juce_dsp/juce_dsp.h>
#include <atomic>
#include <random>

static constexpr int NUM_TRACKS = 10;
static constexpr int MAX_VOICES = 32;
static constexpr int BASE_NOTE = 60;

class TrommelkisteProcessor : public juce::AudioProcessor
{
public:
    TrommelkisteProcessor();
    ~TrommelkisteProcessor() 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 "Trommelkiste"; }
    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;

    std::optional<juce::String> getNameForMidiNoteNumber(int note, int) override;

    void loadSample(int trackIndex, const juce::File& file);
    void clearSample(int trackIndex);

    struct Track
    {
        juce::AudioBuffer<float> buffer;
        int fileSampleRate = 44100;
        juce::SpinLock lock;
        juce::String filePath;
    };

    Track tracks[NUM_TRACKS];
    std::atomic<bool> trackActive[NUM_TRACKS] = {};
    std::atomic<float> outputLevel { 0.0f };
    juce::AudioProcessorValueTreeState apvts;

    // Sample library
    struct SampleRef
    {
        juce::String name;
        juce::File file;
        const char* data = nullptr;
        int dataSize = 0;
    };

    juce::Array<SampleRef> trackSamples[NUM_TRACKS];
    int currentSampleIndex[NUM_TRACKS] = {};
    void scanSamples();
    void loadSampleByIndex(int trackIndex, int sampleIndex);
    void nextSample(int trackIndex);
    void prevSample(int trackIndex);

    // Kit / Preset system
    struct Kit
    {
        juce::String name;
        const char* samples[NUM_TRACKS];
    };

    int currentKitIndex = 0;
    void loadKit(int index);
    void nextKit();
    void prevKit();
    int getNumKits() const;
    juce::String getCurrentKitName() const;
    static const Kit* getKitList();

private:
    struct Voice
    {
        bool active = false;
        int trackIndex = -1;
        float position = 0.0f;
        int samplesPlayed = 0;
        float filterStateL = 0.0f;
        float filterStateR = 0.0f;
        float noteVelocity = 1.0f;
        float ringModPhase = 0.0f;
    };

    Voice voices[MAX_VOICES];
    juce::AudioFormatManager formatManager;
    double currentSampleRate = 44100.0;
    std::mt19937 rng{std::random_device{}()};

    // Effects
    static constexpr int DELAY_BUFFER_SECONDS = 4;
    juce::AudioBuffer<float> delayBuffer;
    int delayWritePos = 0;
    juce::dsp::Reverb reverb;
    juce::dsp::Reverb::Parameters reverbParams;

    int findFreeVoice(int trackIndex);
    void handleNoteOn(int trackIndex, float velocity);
    void handleNoteOff(int trackIndex);
    juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();

    struct PendingShuffleEvent
    {
        int trackIndex = -1;
        float velocity = 0.0f;
        int delaySamples = 0;
    };

    static constexpr int MAX_PENDING_SHUFFLE = 32;
    PendingShuffleEvent pendingShuffle[MAX_PENDING_SHUFFLE];
    int numPendingShuffle = 0;
    int lastTriggeredTrack = -1;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TrommelkisteProcessor)
};
