#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_dsp/juce_dsp.h>
#include "PluginProcessor.h"
#include <vector>

class KnobLookAndFeel : public juce::LookAndFeel_V4 {
public:
    KnobLookAndFeel();
    void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                          float sliderPos, float rotaryStartAngle,
                          float rotaryEndAngle, juce::Slider& slider) override;
private:
    juce::Image knobOverlay;
};

class ComboBoxLookAndFeel : public juce::LookAndFeel_V4 {
public:
    void drawComboBox(juce::Graphics& g, int width, int height,
                      bool isButtonDown, int buttonX, int buttonY,
                      int buttonW, int buttonH, juce::ComboBox& box) override;
    void drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area,
                           bool isSeparator, bool isActive, bool isHighlighted,
                           bool isTicked, bool hasSubMenu, const juce::String& text,
                           const juce::String& shortcutKeyText,
                           const juce::Drawable* icon, const juce::Colour* textColour) override;
};

class VuMeter : public juce::Component, public juce::Timer {
public:
    explicit VuMeter(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
    void paint(juce::Graphics& g) override;
    void timerCallback() override { repaint(); }
private:
    ChromaFlockProcessor& processor;
};

class WaveformDisplay : public juce::Component, public juce::Timer {
public:
    explicit WaveformDisplay(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
    void paint(juce::Graphics& g) override;
    void timerCallback() override { repaint(); }
private:
    ChromaFlockProcessor& processor;
};

class SpectrumAnalyzer : public juce::Component, public juce::Timer {
public:
    explicit SpectrumAnalyzer(ChromaFlockProcessor& p) : processor(p) {
        fft = std::make_unique<juce::dsp::FFT>(ChromaFlockProcessor::fftOrder);
        startTimerHz(30);
    }
    void paint(juce::Graphics& g) override;
    void timerCallback() override { repaint(); }
private:
    ChromaFlockProcessor& processor;
    std::unique_ptr<juce::dsp::FFT> fft;
};

class PatchLCD : public juce::Component {
public:
    std::function<void()> onClick;
    void setText(const juce::String& t) { text = t; repaint(); }
    const juce::String& getText() const { return text; }
    void paint(juce::Graphics& g) override;
    void mouseEnter(const juce::MouseEvent&) override { setMouseCursor(juce::MouseCursor::PointingHandCursor); }
    void mouseDown(const juce::MouseEvent&) override { if (onClick) onClick(); }
private:
    juce::String text = "INIT";
};

class MidiLed : public juce::Component, public juce::Timer {
public:
    explicit MidiLed(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
    void paint(juce::Graphics& g) override;
    void timerCallback() override { repaint(); }
private:
    ChromaFlockProcessor& processor;
};

class PianoRollComponent : public juce::Component, public juce::Timer {
public:
    explicit PianoRollComponent(ChromaFlockProcessor& p) : processor(p) { startTimerHz(60); }
    void paint(juce::Graphics& g) override;
    void timerCallback() override;
    void mouseDown(const juce::MouseEvent& e) override;
    void mouseUp(const juce::MouseEvent& e) override;
    void mouseDrag(const juce::MouseEvent& e) override;

private:
    ChromaFlockProcessor& processor;

    static constexpr int firstMidiNote = 48; // C3
    static constexpr int numKeys = 49;       // C3..C7

    int whiteKeyWidth = 54;
    int whiteKeyHeight = 130;
    int blackKeyWidth = 35;
    int blackKeyHeight = 80;
    int pitchBendWidth = 40;

    int midiNoteForKey(int index) const;
    bool isBlackKey(int midiNote) const;
    int keyAtPosition(juce::Point<int> pos) const;
    void triggerNote(int note, bool on);

    int lastTriggeredNote = -1;
    int currentPitchBend = 64; // 0..127, 64 = center
    bool pitchBendDragging = false;
    int pitchBendStartY = 0;

    bool pitchBendGliding = false;
    float pitchBendGlideStart = 0.0f;
    juce::int64 pitchBendGlideStartMs = 0;
    static constexpr int pitchBendGlideMs = 50;
};

class MainContentComponent : public juce::Component {
public:
    explicit MainContentComponent(ChromaFlockProcessor& p);
    void paint(juce::Graphics& g) override;
    void resized() override;

    std::function<void(float)> onScaleChanged;

private:
    ChromaFlockProcessor& processorRef;
    KnobLookAndFeel knobLaf;
    ComboBoxLookAndFeel comboLaf;
    juce::Image bgImage;
    bool bgImageLoaded = false;
    juce::Image logoImage;
    bool logoLoaded = false;

    VuMeter vuMeter;
    WaveformDisplay waveformDisplay;
    SpectrumAnalyzer spectrumAnalyzer;
    PianoRollComponent pianoRoll;

    juce::Label osc1Label, osc2Label, filterLabel, envLabel, fEnvLabel, globalLabel;
    juce::Label fxLabel, lfoLabel, fx2Label;
    juce::Label scaleLabel;
    juce::Label midiLabel;
    juce::Label octaveTransposeLabel, semitoneTransposeLabel;

    juce::ComboBox octaveTransposeBox, semitoneTransposeBox;
    juce::ComboBox osc1WaveBox;
    juce::Slider osc1OctKnob, osc1SemiKnob, osc1FineKnob, osc1LevelKnob;

    juce::ComboBox osc2WaveBox;
    juce::Slider osc2OctKnob, osc2SemiKnob, osc2FineKnob, osc2LevelKnob, phaseOffsetKnob;

    juce::ComboBox filterTypeBox;
    juce::Slider filterCutoffKnob, filterResKnob, filterEnvAmtKnob, keyTrackKnob;

    juce::Slider envAttackKnob, envDecayKnob, envSustainKnob, envReleaseKnob;
    juce::Slider fEnvAttackKnob, fEnvDecayKnob, fEnvSustainKnob, fEnvReleaseKnob;

    juce::Slider panKnob, driveKnob, masterKnob, pbRangeKnob;

    // FX controls
    juce::ComboBox distTypeBox;
    juce::Slider distAmountKnob, distSymmetryKnob, distToneKnob;
    juce::Slider compThresholdKnob, compRatioKnob, compAttackKnob, compReleaseKnob, compMakeupKnob;
    juce::Slider autoPanRateKnob, autoPanDepthKnob, autoPanPhaseKnob;
    juce::Slider limiterThresholdKnob, limiterCeilingKnob, limiterReleaseKnob;

    // LFO controls
    juce::Slider lfo1RateKnob, lfo1DepthKnob;
    juce::ComboBox lfo1ShapeBox, lfo1DestBox, lfo1SyncBox;
    juce::Slider lfo2RateKnob, lfo2DepthKnob;
    juce::ComboBox lfo2ShapeBox, lfo2DestBox, lfo2SyncBox;

    // FX2 controls
    juce::Slider delayTimeKnob, delayFbKnob, delayMixKnob, delayPingPongKnob;
    juce::ComboBox delaySyncBox;
    juce::Slider reverbSizeKnob, reverbDampKnob, reverbMixKnob;

    // Preset menu
    juce::TextButton presetButton{"PRESET"};
    PatchLCD patchLCD;
    MidiLed midiLed;
    juce::TextButton prevPresetButton{"<"}, nextPresetButton{">"};
    juce::TextButton randomizeButton{"RANDOM"};
    int currentPresetIndex = 0;

    juce::ComboBox uiScaleBox;

    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;

    std::unique_ptr<SliderAttachment> osc1OctAttach, osc1SemiAttach, osc1FineAttach, osc1LevelAttach;
    std::unique_ptr<SliderAttachment> osc2OctAttach, osc2SemiAttach, osc2FineAttach, osc2LevelAttach, phaseOffsetAttach;
    std::unique_ptr<ComboBoxAttachment> osc1WaveAttach, osc2WaveAttach, filterTypeAttach;
    std::unique_ptr<ComboBoxAttachment> octaveTransposeAttach, semitoneTransposeAttach;
    std::unique_ptr<SliderAttachment> filterCutoffAttach, filterResAttach, filterEnvAmtAttach, keyTrackAttach;
    std::unique_ptr<SliderAttachment> envAttackAttach, envDecayAttach, envSustainAttach, envReleaseAttach;
    std::unique_ptr<SliderAttachment> fEnvAttackAttach, fEnvDecayAttach, fEnvSustainAttach, fEnvReleaseAttach;
    std::unique_ptr<SliderAttachment> panAttach, driveAttach, masterAttach, pbRangeAttach;

    // FX attachments
    std::unique_ptr<ComboBoxAttachment> distTypeAttach;
    std::unique_ptr<SliderAttachment> distAmountAttach, distSymmetryAttach, distToneAttach;
    std::unique_ptr<SliderAttachment> compThresholdAttach, compRatioAttach, compAttackAttach;
    std::unique_ptr<SliderAttachment> compReleaseAttach, compMakeupAttach;
    std::unique_ptr<SliderAttachment> autoPanRateAttach, autoPanDepthAttach, autoPanPhaseAttach;
    std::unique_ptr<SliderAttachment> limiterThresholdAttach, limiterCeilingAttach, limiterReleaseAttach;

    // LFO attachments
    std::unique_ptr<SliderAttachment> lfo1RateAttach, lfo1DepthAttach, lfo1BeatAttach;
    std::unique_ptr<ComboBoxAttachment> lfo1ShapeAttach, lfo1DestAttach, lfo1SyncAttach;
    std::unique_ptr<SliderAttachment> lfo2RateAttach, lfo2DepthAttach, lfo2BeatAttach;
    std::unique_ptr<ComboBoxAttachment> lfo2ShapeAttach, lfo2DestAttach, lfo2SyncAttach;

    // FX2 attachments
    std::unique_ptr<SliderAttachment> delayTimeAttach, delayFbAttach, delayMixAttach, delayBeatAttach;
    std::unique_ptr<ComboBoxAttachment> delaySyncAttach;
    std::unique_ptr<SliderAttachment> delayPingPongAttach;
    std::unique_ptr<SliderAttachment> reverbSizeAttach, reverbDampAttach, reverbMixAttach;

    void setupLabel(juce::Label& label, const juce::String& text);
    void setupKnob(juce::Slider& knob, const juce::String& name);
    void setupCombo(juce::ComboBox& combo);
    void setupSyncKnob(juce::Slider& knob,
                       std::unique_ptr<SliderAttachment>& rateAttach,
                       std::unique_ptr<SliderAttachment>& beatAttach,
                       const juce::String& rateId,
                       const juce::String& beatId,
                       juce::ComboBox& syncBox);
    void showPresetMenu();
    void showFileMenu();
    void loadPresetFile();
    void savePresetFile();
    juce::String currentPresetName() const;
    void applyCurrentPreset();
    void randomizeParameters();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
};

class ChromaFlockEditor : public juce::AudioProcessorEditor {
public:
    ChromaFlockEditor(ChromaFlockProcessor& p);
    ~ChromaFlockEditor() override;

    void paint(juce::Graphics&) override;
    void resized() override;
    void setUIScale(float newScale);

private:
    MainContentComponent content;
    float currentScale = 1.0f;
    static constexpr int baseWidth = 1660;
    static constexpr int baseHeight = 1028;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockEditor)
};
