#pragma once

#include <juce_core/juce_core.h>

#include <cstdint>

namespace deeppluck
{
struct DeepPluckEnvelopeParameters
{
    float attackSeconds = 0.003f;
    float decaySeconds = 0.320f;
    float sustainLevel = 0.0f;
    float releaseSeconds = 0.180f;
};

class DeepPluckEnvelope final
{
public:
    enum class Contour : std::uint8_t { amplitude, filter };
    explicit DeepPluckEnvelope (Contour contour = Contour::amplitude) noexcept;

    enum class Stage : std::uint8_t
    {
        idle,
        attack,
        decay,
        sustain,
        release
    };

    void prepare (double newSampleRate) noexcept;
    void reset() noexcept;
    void setParameters (DeepPluckEnvelopeParameters newParameters) noexcept;
    void noteOn() noexcept;
    void noteOff() noexcept;
    [[nodiscard]] float getNextSample() noexcept;
    [[nodiscard]] bool isActive() const noexcept;
    [[nodiscard]] bool isReleasing() const noexcept { return stage == Stage::release; }
    [[nodiscard]] float getCurrentLevel() const noexcept { return currentValue; }
    [[nodiscard]] Stage getStage() const noexcept { return stage; }

private:
    void enterStage (Stage newStage, float destination, float durationSeconds,
                     float targetRatio) noexcept;
    void completeCurrentStage() noexcept;
    [[nodiscard]] int durationInSamples (float durationSeconds) const noexcept;

    DeepPluckEnvelopeParameters parameters;
    const float decayReleaseTargetRatio;
    double sampleRate = 44100.0;
    float currentValue = 0.0f;
    float recurrenceTarget = 0.0f;
    float coefficient = 0.0f;
    float stageMinimum = 0.0f;
    float stageMaximum = 1.0f;
    int samplesRemaining = 0;
    Stage stage = Stage::idle;
};
}
