/*
  ==============================================================================

    ModFilter.h
    Created: 24 Jul 2026
    Author:  Armin

  ==============================================================================
*/

#pragma once
#include <JuceHeader.h>

#include "Effect.h"

enum class ModFilterType : std::uint8_t
{
    LP12,
    LP24,
    HP,
    BP,
    Notch
};

/** A multi-mode filter effect for the modulation section. Supports LP12, LP24, HP, BP, Notch. */
class ModFilter final : public Effect
{
public:
    void initialize(int numChannels, int fxSampleRate) override
    {
        sampleRate = fxSampleRate;

        juce::dsp::ProcessSpec spec{};
        spec.numChannels = static_cast<juce::uint32>(numChannels);
        spec.sampleRate = sampleRate;
        filterChain.reset();
        filterChain.prepare(spec);

        currentCutoff = -1.f;
        currentResonance = -1.f;
        currentType = static_cast<ModFilterType>(255);  // Force update
    }

    void setFilterType(ModFilterType type)
    {
        if (type != currentType)
        {
            currentType = type;
            needsUpdate = true;
        }
    }

    void setCutoff(float cutoff)
    {
        cutoff = juce::jlimit(20.f, float(sampleRate) / 2.f - 10.f, cutoff);
        if (std::abs(cutoff - currentCutoff) > 0.1f)
        {
            currentCutoff = cutoff;
            needsUpdate = true;
        }
    }

    void setResonance(float resonance)
    {
        resonance = juce::jlimit(0.f, 1.f, resonance);
        if (std::abs(resonance - currentResonance) > 0.001f)
        {
            currentResonance = resonance;
            needsUpdate = true;
        }
    }

    void updateParams(const SamplerParameters& samplerSound, bool /*modulating*/) override
    {
        setFilterType(static_cast<ModFilterType>(samplerSound.modFilterType->getIndex()));
        setCutoff(samplerSound.modFilterCutoff->get());
        setResonance(samplerSound.modFilterResonance->get());
    }

    void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample = 0) override
    {
        if (needsUpdate)
            updateCoefficients();

        juce::dsp::AudioBlock block{ buffer.getArrayOfWritePointers(), size_t(buffer.getNumChannels()), size_t(startSample), size_t(numSamples) };
        juce::dsp::ProcessContextReplacing context{ block };
        filterChain.process(context);
    }

private:
    void updateCoefficients()
    {
        needsUpdate = false;

        float q = 0.707f / (1.f - currentResonance * 0.9f);  // Map 0-1 resonance to Q
        q = juce::jlimit(0.5f, 30.f, q);

        switch (currentType)
        {
        case ModFilterType::LP12:
        {
            auto coeff = juce::dsp::IIR::Coefficients<float>::makeLowPass(sampleRate, currentCutoff, q);
            *filterChain.get<0>().state = *coeff;
            *filterChain.get<1>().state = *coeff;
            break;
        }
        case ModFilterType::LP24:
        {
            auto coeff = juce::dsp::IIR::Coefficients<float>::makeLowPass(sampleRate, currentCutoff, q);
            *filterChain.get<0>().state = *coeff;
            *filterChain.get<1>().state = *coeff;
            *filterChain.get<2>().state = *coeff;
            *filterChain.get<3>().state = *coeff;
            break;
        }
        case ModFilterType::HP:
        {
            auto coeff = juce::dsp::IIR::Coefficients<float>::makeHighPass(sampleRate, currentCutoff, q);
            *filterChain.get<0>().state = *coeff;
            *filterChain.get<1>().state = *coeff;
            break;
        }
        case ModFilterType::BP:
        {
            auto coeff = juce::dsp::IIR::Coefficients<float>::makeBandPass(sampleRate, currentCutoff, q);
            *filterChain.get<0>().state = *coeff;
            *filterChain.get<1>().state = *coeff;
            break;
        }
        case ModFilterType::Notch:
        {
            auto coeff = juce::dsp::IIR::Coefficients<float>::makeNotch(sampleRate, currentCutoff, q);
            *filterChain.get<0>().state = *coeff;
            *filterChain.get<1>().state = *coeff;
            break;
        }
        }
    }

    using Filter = juce::dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>, juce::dsp::IIR::Coefficients<float>>;
    using FilterChain = juce::dsp::ProcessorChain<Filter, Filter, Filter, Filter>;

    int sampleRate{ 0 };
    FilterChain filterChain;

    ModFilterType currentType{ ModFilterType::LP12 };
    float currentCutoff{ -1.f };
    float currentResonance{ -1.f };
    bool needsUpdate{ true };
};
