#include "fx_phaser.h"

#include <algorithm>
#include <cmath>

Phaser::AllpassDelay::AllpassDelay() :
    a1_(0.0f)
{
    this->reset();
}

Phaser::AllpassDelay::~AllpassDelay()
{
}

void Phaser::AllpassDelay::reset()
{
    memset(this->z_, 0, 2 * sizeof(float32_t));
}

void Phaser::AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
    outL = inL * -this->a1_ + this->z_[0];
    this->z_[0] = outL * this->a1_ + inL;

    outR = inR * -this->a1_ + this->z_[1];
    this->z_[1] = outR * this->a1_ + inR;
}

void Phaser::AllpassDelay::setDelay(float32_t delay)
{
    this->a1_ = (1.0f - delay) / (1.0f + delay);
}


Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback, unsigned nb_stages) : 
    FXElement(sampling_rate),
    lfo_(sampling_rate, 0.0f, 2.5f),
    depth_(0.0f),
    feedback_(0.0f),
    dmin_(0.0f),
    dmax_(0.0f)
{
    this->setRate(rate);
    this->setDepth(depth);
    this->setFeedback(feedback);
    this->setNbStages(nb_stages);
    this->setFrequencyRange(440.0f, 1600.0f);

    this->reset();
}

Phaser::~Phaser()
{
}

void Phaser::reset()
{
    memset(this->z_, 0, 2 * sizeof(float32_t));

    for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
    {
        this->stages_[i].reset();
    }
    this->lfo_.reset();
}

void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
    float32_t d = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_.process()) / 2.0f);

    float32_t sampleL = inL + this->feedback_ * this->z_[0];
    float32_t sampleR = inR + this->feedback_ * this->z_[1];
    for(unsigned i = 0; i < this->nb_stages_; ++i)
    {
        this->stages_[i].setDelay(d);

        this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR);
    }
    this->z_[0] = sampleL;
    this->z_[1] = sampleR;

    outL = inL + this->z_[0] * this->depth_;
    outR = inR + this->z_[1] * this->depth_;
}

void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency)
{
    this->dmin_ = 2.0f * std::min(min_frequency, max_frequency) / this->getSamplingRate();
    this->dmax_ = 2.0f * std::max(min_frequency, max_frequency) / this->getSamplingRate();
}

void Phaser::setRate(float32_t rate)
{
    rate = constrain(rate, 0.0f, 1.0f);
    this->lfo_.setNormalizedFrequency(rate);
}

inline float32_t Phaser::getRate() const
{
    return this->lfo_.getNormalizedFrequency();
}

void Phaser::setDepth(float32_t depth)
{
    depth = constrain(depth, 0.0f, 1.0f);
    this->depth_ = depth;
}

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

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

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

void Phaser::setNbStages(unsigned nb_stages)
{
    if(nb_stages < 2)
    {
        nb_stages = 2;
    }
    else if(nb_stages > MAX_NB_PHASES)
    {
        nb_stages = MAX_NB_PHASES;
    }
    this->nb_stages_ = nb_stages;
}

unsigned Phaser::getNbStages() const
{
    return this->nb_stages_;
}
