#pragma once
#include <cmath>
#include <algorithm>

enum class Waveform { Sine = 0, Saw, Square, Triangle, Noise, NumWaveforms };

class Oscillator {
public:
    void prepare(double sampleRate) {
        this->sampleRate = sampleRate;
        phase = 0.0;
        phaseIncrement = 0.0;
    }

    void setFrequency(float freq) {
        frequency = freq;
        if (sampleRate > 0.0)
            phaseIncrement = frequency / static_cast<float>(sampleRate);
    }

    void setWaveform(Waveform wf) { waveform = wf; }
    void setLevel(float l) { level = std::clamp(l, 0.0f, 1.0f); }
    void setPan(float p) { pan = std::clamp(p, -1.0f, 1.0f); }
    void setSemitone(float s) { semitoneOffset = s; }
    void setFineTune(float f) { fineTune = f; }
    void setPhase(float p) { phase = p; }

    float getFrequency() const { return frequency; }

    void process(float* leftOut, float* rightOut) {
        float output = 0.0f;

        switch (waveform) {
            case Waveform::Sine:
                output = std::sin(phase * 2.0f * pi);
                break;
            case Waveform::Saw:
                output = 2.0f * (phase - 0.5f);
                break;
            case Waveform::Square:
                output = (phase < 0.5f) ? 1.0f : -1.0f;
                break;
            case Waveform::Triangle:
                output = 4.0f * std::abs(phase - 0.5f) - 1.0f;
                break;
            case Waveform::Noise:
                output = random.nextFloat() * 2.0f - 1.0f;
                break;
            default:
                output = 0.0f;
                break;
        }

        phase += phaseIncrement;
        if (phase >= 1.0f) phase -= 1.0f;

        output *= level;

        float leftGain = std::sqrt((1.0f - pan) * 0.5f);
        float rightGain = std::sqrt((1.0f + pan) * 0.5f);

        *leftOut += output * leftGain;
        *rightOut += output * rightGain;
    }

    float getCurrentPhase() const { return phase; }

private:
    static constexpr float pi = 3.14159265358979323846f;
    float frequency = 440.0f;
    float phase = 0.0f;
    float phaseIncrement = 0.0f;
    float level = 0.7f;
    float pan = 0.0f;
    float semitoneOffset = 0.0f;
    float fineTune = 0.0f;
    double sampleRate = 44100.0;
    Waveform waveform = Waveform::Saw;
    juce::Random random;
};
