namespace QuanTAlib;

//https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html

public class Rema : AbstractBase
{
    private readonly int _period;
    private readonly double _lambda;
    private double _lastRema, _prevRema;
    private double _savedLastRema, _savedPrevRema;

    public int Period => _period;
    public double Lambda => _lambda;

    public Rema(int period, double lambda = 0.5) : base()
    {
        if (period < 1)
            throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
        if (lambda < 0)
            throw new ArgumentOutOfRangeException(nameof(lambda), "Lambda must be non-negative.");

        _period = period;
        _lambda = lambda;
        Name = $"REMA({period},{lambda:F2})";
        WarmupPeriod = period;
        Init();
    }

    public override void Init()
    {
        base.Init();
        _lastRema = 0;
        _prevRema = 0;
        _savedLastRema = 0;
        _savedPrevRema = 0;
    }

    protected override void ManageState(bool isNew)
    {
        if (isNew)
        {
            _savedLastRema = _lastRema;
            _savedPrevRema = _prevRema;
            _index++;
        }
        else
        {
            _lastRema = _savedLastRema;
            _prevRema = _savedPrevRema;
        }
    }

    protected override double Calculation()
    {
        ManageState(Input.IsNew);

        double alpha = 2.0 / (Math.Min(_period, _index) + 1);

        if (_index > 2)
        {
            double rema = (_lastRema + alpha * (Input.Value - _lastRema) + _lambda * (_lastRema + (_lastRema - _prevRema))) / (1 + _lambda);
            _prevRema = _lastRema;
            _lastRema = rema;
        }
        else if (_index == 2)
        {
            _prevRema = _lastRema;
            _lastRema = Input.Value;
        }
        else
        { // _index == 1
            _lastRema = Input.Value;
        }

        IsHot = _index >= WarmupPeriod;
        return _lastRema;
    }
}