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

    adsr.cpp
    Created: 25 Oct 2025 2:09:32pm
    Author:  mickl

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

#include "adsr.h"
#include "math.h"

#ifndef M_E
#define M_E 2.71828182845904523536
#endif


void Adsr::Init(float sample_rate, int blockSize)
{
    sample_rate_ = sample_rate / blockSize;
    attackShape_ = -1.f;
    attackTarget_ = 0.0f;
    attackTime_ = -1.f;
    decayTime_ = -1.f;
    releaseTime_ = -1.f;
    sus_level_ = 0.7f;
    x_ = 0.0f;
    gate_ = false;
    mode_ = ADSR_SEG_IDLE;

    SetTime(ADSR_SEG_ATTACK, 0.1f);
    SetTime(ADSR_SEG_DECAY, 0.1f);
    SetTime(ADSR_SEG_RELEASE, 0.1f);
}

void Adsr::Retrigger(bool hard)
{
    mode_ = ADSR_SEG_ATTACK;
    if (hard)
        x_ = 0.f;
}

void Adsr::SetTime(int seg, float time)
{
    switch (seg)
    {
    case ADSR_SEG_ATTACK: SetAttackTime(time, 0.0f); break;
    case ADSR_SEG_DECAY:
    {
        SetTimeConstant(time, decayTime_, decayD0_);
    }
    break;
    case ADSR_SEG_RELEASE:
    {
        SetTimeConstant(time, releaseTime_, releaseD0_);
    }
    break;
    default: return;
    }
}

void Adsr::SetAttackTime(float timeInS, float shape)
{
    if ((timeInS != attackTime_) || (shape != attackShape_))
    {
        attackTime_ = timeInS;
        attackShape_ = shape;
        if (timeInS > 0.f)
        {
            float x = shape;
            float target = 9.f * powf(x, 10.f) + 0.3f * x + 1.01f;
            attackTarget_ = target;
            float logTarget = logf(1.f - (1.f / target)); // -1 for decay
            attackD0_ = 1.f - expf(logTarget / (timeInS * sample_rate_));
        }
        else
            attackD0_ = 1.f; // instant change
    }
}
void Adsr::SetDecayTime(float timeInS)
{
    SetTimeConstant(timeInS, decayTime_, decayD0_);
}
void Adsr::SetReleaseTime(float timeInS)
{
    SetTimeConstant(timeInS, releaseTime_, releaseD0_);
}


void Adsr::SetTimeConstant(float timeInS, float& time, float& coeff)
{
    if (timeInS != time)
    {
        time = timeInS;
        if (time > 0.f)
        {
            const float target = logf(1. / M_E);
            coeff = 1.f - expf(target / (time * sample_rate_));
        }
        else
            coeff = 1.f; // instant change
    }
}


float Adsr::Process(bool gate)
{
    float out = 0.0f;

    if (gate && !gate_) // rising edge
        mode_ = ADSR_SEG_ATTACK;
    else if (!gate && gate_) // falling edge
        mode_ = ADSR_SEG_RELEASE;
    gate_ = gate;

    float D0(attackD0_);
    if (mode_ == ADSR_SEG_DECAY)
        D0 = decayD0_;
    else if (mode_ == ADSR_SEG_RELEASE)
        D0 = releaseD0_;

    float target = mode_ == ADSR_SEG_DECAY ? sus_level_ : -0.01f;
    switch (mode_)
    {
    case ADSR_SEG_IDLE: out = 0.0f; break;
    case ADSR_SEG_ATTACK:
        x_ += D0 * (attackTarget_ - x_);
        out = x_;
        if (out > 1.f)
        {
            x_ = out = 1.f;
            mode_ = ADSR_SEG_DECAY;
        }
        break;
    case ADSR_SEG_DECAY:
    case ADSR_SEG_RELEASE:
        x_ += D0 * (target - x_);
        out = x_;
        if (out < 0.0f)
        {
            x_ = out = 0.f;
            mode_ = ADSR_SEG_IDLE;
        }
    default: break;
    }
    return out;
}