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

    Phaser.h
    Created: 24 Jul 2026
    Author:  Armin

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

#pragma once
#include <JuceHeader.h>

#include "Effect.h"

/** A phaser effect with configurable stages (pairs of all-pass filters) and LFO modulation. */
class PhaserEffect final : public Effect
{
public:
    void initialize(int numChannels, int fxSampleRate) override
    {
        sampleRate = fxSampleRate;
        numChannelsUsed = numChannels;

        for (auto& stage : stages)
        {
            stage[0].reset();
            stage[1].reset();
        }
        feedbackSampleL = 0.f;
        feedbackSampleR = 0.f;

        currentCenterFreq = -1.f;
        currentStages = -1;
    }

    void setCenterFrequency(float freq)
    {
        freq = juce::jlimit(50.f, float(sampleRate) / 4.f, freq);
        if (std::abs(freq - currentCenterFreq) > 0.5f)
        {
            currentCenterFreq = freq;
            updateAllPassCoefficients();
        }
    }

    void setNumStages(int numStages)
    {
        numStages = juce::jlimit(1, 6, numStages);
        if (numStages != currentStages)
        {
            currentStages = numStages;
            updateAllPassCoefficients();
        }
    }

    void setFeedback(float fb)
    {
        feedback = juce::jlimit(-0.95f, 0.95f, fb);
    }

    void setDepth(float d)
    {
        depth = juce::jlimit(0.f, 1.f, d);
    }

    /** Process the buffer with a given LFO modulation value [-1, 1].
        This is called per block; the center frequency is modulated by the LFO depth.
    */
    void processWithLFO(juce::AudioBuffer<float>& buffer, int numSamples, float lfoValue)
    {
        float modulatedFreq = currentCenterFreq * std::pow(2.f, lfoValue * depth * 3.f);
        setCenterFrequency(modulatedFreq);

        for (int ch = 0; ch < buffer.getNumChannels(); ch++)
        {
            auto* data = buffer.getWritePointer(ch);
            float& fbSample = (ch == 0) ? feedbackSampleL : feedbackSampleR;

            for (int i = 0; i < numSamples; i++)
            {
                float input = data[i] + fbSample * feedback;

                float apOutput = input;
                for (int s = 0; s < currentStages; s++)
                {
                    auto& stage = stages[static_cast<size_t>(s)];
                    auto& filter = (ch == 0) ? stage[0] : stage[1];
                    apOutput = filter.process(apOutput);
                }

                fbSample = apOutput;
                data[i] = input + apOutput * (-depth);
            }
        }
    }

    void updateParams(const SamplerParameters& samplerSound, bool /*modulating*/) override
    {
        setFeedback(samplerSound.modPhaserFeedback->get());
        setNumStages(samplerSound.modPhaserStages->get());
        setDepth(samplerSound.modPhaserDepth->get());
    }

    void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample = 0) override
    {
        // When called without LFO, process with no modulation (center freq stays)
        for (int ch = 0; ch < buffer.getNumChannels(); ch++)
        {
            auto* data = buffer.getWritePointer(ch, startSample);
            float& fbSample = (ch == 0) ? feedbackSampleL : feedbackSampleR;

            for (int i = 0; i < numSamples; i++)
            {
                float input = data[i] + fbSample * feedback;

                float apOutput = input;
                for (int s = 0; s < currentStages; s++)
                {
                    auto& stage = stages[static_cast<size_t>(s)];
                    auto& filter = (ch == 0) ? stage[0] : stage[1];
                    apOutput = filter.process(apOutput);
                }

                fbSample = apOutput;
                data[i] = input + apOutput * (-depth);
            }
        }
    }

private:
    /** Simple 1st-order all-pass filter: y[n] = b0*x[n] + b1*x[n-1] - a1*y[n-1] */
    struct AllPassFilter
    {
        void reset() { x1 = 0.f; y1 = 0.f; }
        void setCoefficients(float a, float b) { a1 = a; b1 = b; }
        float process(float input)
        {
            float output = b1 * input + b0 * x1 - a1 * y1;
            y1 = output;
            x1 = input;
            return output;
        }

        float b0{ 1.f };
        float b1{ 0.f };
        float a1{ 0.f };
        float x1{ 0.f };
        float y1{ 0.f };
    };

    void updateAllPassCoefficients()
    {
        if (sampleRate <= 0)
            return;

        for (int s = 0; s < currentStages; s++)
        {
            float freq = currentCenterFreq * (1.f + float(s) * 0.3f);
            freq = juce::jlimit(50.f, float(sampleRate) / 4.f - 10.f, freq);

            float angle = juce::MathConstants<float>::pi * freq / float(sampleRate);
            float t = std::tan(angle);
            float a = (t - 1.f) / (t + 1.f);

            allPassA[static_cast<size_t>(s)] = a;
            allPassB[static_cast<size_t>(s)] = 1.f;

            stages[static_cast<size_t>(s)][0].setCoefficients(a, 1.f);
            stages[static_cast<size_t>(s)][1].setCoefficients(a, 1.f);
        }
    }

    static constexpr int MAX_STAGES{ 6 };
    std::array<std::array<AllPassFilter, 2>, MAX_STAGES> stages;
    std::array<float, MAX_STAGES> allPassA{};
    std::array<float, MAX_STAGES> allPassB{};

    int sampleRate{ 0 };
    int numChannelsUsed{ 2 };
    int currentStages{ 3 };
    float currentCenterFreq{ 1000.f };
    float feedback{ 0.f };
    float depth{ 0.5f };
    float feedbackSampleL{ 0.f };
    float feedbackSampleR{ 0.f };
};
