#pragma once
#include <vector>
#include <algorithm>
#include <cmath>

class Delay {
public:
    void prepare(double sr) {
        sampleRate = sr;
        int maxSamples = static_cast<int>(sr * 2.0);
        bufferL.resize(maxSamples, 0.0f);
        bufferR.resize(maxSamples, 0.0f);
        writePos = 0;
    }

    void setParameters(float timeMs, float fb, float mx, bool pingpong = false,
                       bool invert = false, bool flatten = false) {
        timeSamplesF = timeMs * 0.001f * static_cast<float>(sampleRate);
        float bufMax = static_cast<float>(bufferL.size()) - 1.0f;
        timeSamplesF = std::clamp(timeSamplesF, 1.0f, bufMax);
        feedback = std::clamp(fb, 0.0f, 0.95f);
        mix = std::clamp(mx, 0.0f, 1.0f);
        pingPong = pingpong;
        pingSign = invert ? -1 : 1;
        // Flatten reduces the bounce *width* by 60% (0.4 of full) without
        // touching the echo tail — tail length is governed solely by feedback.
        pingStrength = flatten ? 0.4f : 1.0f;
        timeSamplesI = static_cast<int>(std::round(timeSamplesF));
        if (timeSamplesI < 1) timeSamplesI = 1;
    }

    void process(float& left, float& right) {
        int bufSize = static_cast<int>(bufferL.size());

        float readPosF = static_cast<float>(writePos) - timeSamplesF;
        while (readPosF < 0.0f) readPosF += static_cast<float>(bufSize);

        int i0 = static_cast<int>(readPosF) % bufSize;
        int i1 = (i0 + 1) % bufSize;
        float frac = readPosF - std::floor(readPosF);

        if (pingPong) {
            // Mono echo line. The tail length is set by feedback alone; the
            // ping-pong bounce is applied as alternating L/R panning whose
            // width is scaled by pingStrength, so flattening narrows the image
            // without shortening the number of repeats.
            float out = bufferL[i0] * (1.0f - frac) + bufferL[i1] * frac;

            float in = 0.5f * (left + right);
            bufferL[writePos] = in + out * feedback;

            pingCounter += 1;
            if (pingCounter >= timeSamplesI) {
                pingCounter -= timeSamplesI;
                pingPhase = -pingPhase;
            }
            float s = pingStrength * static_cast<float>(pingPhase * pingSign);
            float lPan = 0.5f + 0.5f * s;
            float rPan = 0.5f - 0.5f * s;

            float wet = out * mix;
            left  = left  * (1.0f - mix) + wet * lPan;
            right = right * (1.0f - mix) + wet * rPan;
        } else {
            float outL = bufferL[i0] * (1.0f - frac) + bufferL[i1] * frac;
            float outR = bufferR[i0] * (1.0f - frac) + bufferR[i1] * frac;

            bufferL[writePos] = left + outL * feedback;
            bufferR[writePos] = right + outR * feedback;

            left  = left  * (1.0f - mix) + outL * mix;
            right = right * (1.0f - mix) + outR * mix;
        }

        writePos = (writePos + 1) % bufSize;
    }

private:
    std::vector<float> bufferL, bufferR;
    int writePos = 0;
    double sampleRate = 44100;
    float timeSamplesF = 8820.0f;
    int timeSamplesI = 8820;
    float feedback = 0.3f;
    float mix = 0.25f;
    bool pingPong = false;
    int pingSign = 1;
    float pingStrength = 1.0f;
    int pingCounter = 0;
    int pingPhase = 1;
};
