#pragma once

#include "DeepPluckChorus.h"
#include "DeepPluckDelay.h"
#include "DeepPluckReverb.h"

#include <array>
#include <atomic>

namespace deeppluck
{
namespace benchmark { struct Access; }
enum class EffectKind : std::uint8_t { chorus, delay, reverb };

inline constexpr std::array<std::array<EffectKind, 3>, 6> effectOrders {{
    { EffectKind::chorus, EffectKind::delay, EffectKind::reverb },
    { EffectKind::chorus, EffectKind::reverb, EffectKind::delay },
    { EffectKind::delay, EffectKind::chorus, EffectKind::reverb },
    { EffectKind::delay, EffectKind::reverb, EffectKind::chorus },
    { EffectKind::reverb, EffectKind::chorus, EffectKind::delay },
    { EffectKind::reverb, EffectKind::delay, EffectKind::chorus }
}};

[[nodiscard]] int getEffectPosition (int order, EffectKind effect) noexcept;
[[nodiscard]] int moveEffectToPosition (int order, EffectKind effect,
                                        int oneBasedPosition) noexcept;

class DeepPluckEffectsRack final
{
public:
    void prepare (double sampleRate, int maximumExpectedSamplesPerBlock);
    void reset() noexcept;
    void requestReset() noexcept;
    void process (juce::AudioBuffer<float>& buffer,
                  const DeepPluckParameterSnapshot& snapshot,
                  double lastValidTempoBpm) noexcept;

    [[nodiscard]] int getCurrentOrder() const noexcept;
    [[nodiscard]] bool isIdle() const noexcept;

private:
    friend struct benchmark::Access;
    enum class BridgePhase : std::uint8_t { stable, fadingOut, fadingIn };

    void processEffect (EffectKind effect, juce::AudioBuffer<float>& buffer,
                        int startSample, int numSamples) noexcept;

    DeepPluckChorus chorus;
    DeepPluckDelay delay;
    DeepPluckReverb reverb;
    juce::AudioBuffer<float> dryBuffer;
    juce::SmoothedValue<float, juce::ValueSmoothingTypes::Linear> orderBridge;
    std::atomic<bool> resetRequested { false };
    int maximumBlockSize = 1;
    int currentOrder = 0;
    int pendingOrder = 0;
    BridgePhase bridgePhase = BridgePhase::stable;
};
}
