#include "fx_flanger.h"

#include <cmath>

Flanger::Flanger(float32_t sampling_rate, float32_t delay_time, float32_t frequency, float32_t depth, float32_t feedback) :
    FXElement(sampling_rate),
    MaxDelayLineSize(static_cast<unsigned>(2.0f * MAX_FLANGER_DELAY * sampling_rate / 1000.0f)),
    delay_line_index_(0),
    lfo_(sampling_rate, LFO::Waveform::Sine, 0.1f, 10.0f)
{
    this->delay_lineL_ = new float32_t[this->MaxDelayLineSize];
    this->delay_lineR_ = new float32_t[this->MaxDelayLineSize];

    this->setDelayTime(delay_time);
    this->setFrequency(frequency);
    this->setDepth(depth);
    this->setFeedback(feedback);
}

Flanger::~Flanger()
{
    delete[] this->delay_lineL_;
    delete[] this->delay_lineR_;
}

void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
    // Calculate the delay time based on the depth and rate parameters
    float32_t delay = this->getDelayTime() + this->getDepth() * this->lfo_.process();

    // Convert the delay time to samples
    unsigned delay_samples = static_cast<unsigned>(delay * this->getSamplingRate() / 1000.0f);

    // mix the input audio with the delayed audio and the feedback signal
    outL = inL + this->delay_lineL_[(this->delay_line_index_ + this->delay_line_size_ - delay_samples) % this->delay_line_size_] * (1.0 - this->getFeedback());
    outR = inR + this->delay_lineR_[(this->delay_line_index_ + this->delay_line_size_ - delay_samples) % this->delay_line_size_] * (1.0 - this->getFeedback());

    // Update the delay buffer with the mixed audio and the feedback signal
    this->delay_lineL_[this->delay_line_index_] = inL + outL * this->getFeedback();
    this->delay_lineR_[this->delay_line_index_] = inR + outR * this->getFeedback();

    this->delay_line_index_ = (this->delay_line_index_ + 1) % this->delay_line_size_;
}

void Flanger::setDelayTime(float32_t delayMS)
{
    this->delay_time_ms_ = constrain(delayMS, 1.0f, MAX_FLANGER_DELAY);
    this->adjustDelayCofficients();
}

float32_t Flanger::getDelayTime() const
{
    return this->delay_time_ms_;
}

void Flanger::setFrequency(float32_t frequency)
{
    this->lfo_.setNormalizedFrequency(frequency);
}

float32_t Flanger::getFrequency() const
{
    return this->lfo_.getNormalizedFrequency();
}

void Flanger::setDepth(float32_t depth)
{
    this->depth_ = constrain(depth, 0.0f, MAX_FLANGER_DELAY);
    this->adjustDelayCofficients();
}

float32_t Flanger::getDepth() const
{
    return this->depth_;
}

void Flanger::setFeedback(float32_t feedback)
{
    this->feedback_ = constrain(feedback, 0.0f, 1.0f);
}

float32_t Flanger::getFeedback() const
{
    return this->feedback_;
}

void Flanger::adjustDelayCofficients()
{
    this->delay_line_size_ = static_cast<unsigned>(this->getSamplingRate() * (this->getDelayTime() + this->getDepth()) / 1000.0f);
}