#pragma once

#include "../Parameters/DeepPluckParameters.h"
#include "../Randomization/DeepPluckRandomization.h"
#include "../Effects/DeepPluckEffectsRack.h"
#include "DeepPluckGuiSupport.h"

#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_gui_basics/juce_gui_basics.h>

#include <functional>
#include <atomic>
#include <limits>
#include <memory>
#include <vector>

class DeepPluckAudioProcessor;

namespace deeppluck::gui
{
enum class Page { main, modulation, effects, settings, sound, filter, shape };

class SliderControl final : public juce::Component
{
public:
    SliderControl (const juce::String& labelText, const juce::String& suffix,
                   bool percentage = false, bool normalized = false);
    void resized() override;
    juce::Slider slider;

private:
    juce::Label label;
};

class ToggleControl final : public juce::Component
{
public:
    explicit ToggleControl (const juce::String& labelText);
    void resized() override;
    juce::ToggleButton button;

private:
    juce::Label label;
};

class ChoiceControl final : public juce::Component
{
public:
    explicit ChoiceControl (const juce::String& labelText);
    void resized() override;
    juce::ComboBox choice;

private:
    juce::Label label;
};

class ModulePanelComponent final : public juce::Component
{
public:
    explicit ModulePanelComponent (const juce::String& titleText);
    void paint (juce::Graphics& graphics) override;
    void resized() override;
    [[nodiscard]] juce::Rectangle<int> getContentBounds() const noexcept;
    void configureRandomization (DeepPluckAudioProcessor& processor,
                                 RandomizationModule module);
    void refreshRandomizationState();

private:
    juce::Label title;
    juce::TextButton randomButton { "R" };
    juce::ToggleButton lockButton { "L" };
    DeepPluckAudioProcessor* owner = nullptr;
    RandomizationModule randomizationModule = RandomizationModule::oscillator1;
};

class HeaderComponent final : public juce::Component
{
public:
    explicit HeaderComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshPresetState();
    void refreshOutputMeter();
    [[nodiscard]] std::array<float, 2> getDisplayedMeterLevels() const noexcept;
    void loadPreviousPreset();
    void loadNextPreset();
    static constexpr int attachmentCount = 2;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    void rebuildPresetCatalog();
    void showSaveDialog (bool saveAs);
    void confirmOverwriteCurrent();
    void confirmDeleteCurrent();
    void saveWithValues (const juce::String& name, const juce::String& author,
                         bool allowOverwrite, bool overwriteCurrentUser);
    void showError (const juce::String& message);

    DeepPluckAudioProcessor& processor;
    juce::AudioProcessorValueTreeState& state;
    juce::Label brandKicker;
    juce::Label title;
    juce::Label version;
    juce::Label presetLabel;
    juce::TextButton previousButton { "<" };
    juce::ComboBox presetSelector;
    juce::TextButton nextButton { ">" };
    juce::TextButton masterRandomButton { "Random" };
    juce::TextButton undoRandomButton { "Undo" };
    juce::Label randomAmountLabel;
    juce::Slider randomAmount;
    juce::TextButton saveButton { "Save" };
    juce::TextButton saveAsButton { "Save As..." };
    juce::TextButton deleteButton { "Delete" };
    juce::TextButton refreshButton { "Refresh" };
    juce::TextButton openFolderButton { "Open Folder" };
    juce::Label statusLabel;
    SliderControl masterTune { "Master Tune", " Hz" };
    SliderControl outputGain { "Output", " dB" };
    StereoPeakMeterComponent outputMeter;
    std::unique_ptr<SliderAttachment> masterTuneAttachment;
    std::unique_ptr<SliderAttachment> outputAttachment;
    std::vector<juce::String> selectorStableIds;
    std::uint64_t lastCatalogGeneration = std::numeric_limits<std::uint64_t>::max();
};

class FinalPageNavigationComponent final : public juce::Component
{
public:
    FinalPageNavigationComponent();
    void resized() override;
    void setSelectedPage (Page page);
    [[nodiscard]] bool isModulationEnabled() const noexcept;
    [[nodiscard]] bool isEffectsEnabled() const noexcept;
    std::function<void (Page)> onPageSelected;

private:
    juce::TextButton mainButton { "MAIN" };
    juce::TextButton modulationButton { "MODULATION" };
    juce::TextButton effectsButton { "FX" };
    juce::TextButton settingsButton { "SETTINGS" };
};

class OscillatorRowComponent final : public juce::Component
{
public:
    explicit OscillatorRowComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 9;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
    ModulePanelComponent oscillator1Panel { "OSCILLATOR 1" };
    ModulePanelComponent oscillator2Panel { "OSCILLATOR 2" };
    ChoiceControl oscillator1Waveform { "Waveform" };
    SliderControl oscillator1PulseWidth { "Pulse Width", {}, false, true };
    WaveformDisplayComponent oscillator1Display { oscillator1Waveform.choice,
                                                  oscillator1PulseWidth.slider };
    ToggleControl oscillator2Enabled { "On" };
    ChoiceControl oscillator2Waveform { "Waveform" };
    SliderControl oscillator2PulseWidth { "Pulse Width", {}, false, true };
    SliderControl oscillator2Level { "Level", " dB" };
    SliderControl oscillator2Octave { "Octave", "" };
    SliderControl oscillator2Semitone { "Semitone", " st" };
    SliderControl oscillator2FineTune { "Fine Tune", " ct" };
    WaveformDisplayComponent oscillator2Display { oscillator2Waveform.choice,
                                                  oscillator2PulseWidth.slider };
    std::unique_ptr<ComboBoxAttachment> oscillator1WaveformAttachment;
    std::unique_ptr<SliderAttachment> oscillator1PulseWidthAttachment;
    std::unique_ptr<ButtonAttachment> oscillator2EnabledAttachment;
    std::unique_ptr<ComboBoxAttachment> oscillator2WaveformAttachment;
    std::unique_ptr<SliderAttachment> oscillator2PulseWidthAttachment;
    std::unique_ptr<SliderAttachment> oscillator2LevelAttachment;
    std::unique_ptr<SliderAttachment> oscillator2OctaveAttachment;
    std::unique_ptr<SliderAttachment> oscillator2SemitoneAttachment;
    std::unique_ptr<SliderAttachment> oscillator2FineTuneAttachment;
};

class MainPageComponent final : public juce::Component
{
public:
    explicit MainPageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 25;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    ModulePanelComponent filterPanel { "FILTER" };
    ModulePanelComponent ampPanel { "AMP ENVELOPE" };
    ModulePanelComponent filterEnvelopePanel { "FILTER ENVELOPE" };
    ModulePanelComponent unisonPanel { "UNISON" };
    ModulePanelComponent transientPanel { "TRANSIENT" };
    ModulePanelComponent noisePanel { "NOISE" };

    ToggleControl filterEnabled { "On" };
    SliderControl cutoff { "Cutoff", " Hz" };
    SliderControl resonance { "Resonance", {}, false, true };
    SliderControl drive { "Drive", " dB" };
    SliderControl keyTracking { "Key Track", {}, false, true };
    SliderControl envAmount { "Env Amount", " oct" };
    SliderControl ampAttack { "Attack", " s" };
    SliderControl ampDecay { "Decay", " s" };
    SliderControl ampSustain { "Sustain", {}, false, true };
    SliderControl ampRelease { "Release", " s" };
    EnvelopeDisplayComponent ampDisplay { ampAttack.slider, ampDecay.slider,
                                          ampSustain.slider, ampRelease.slider };
    SliderControl filterAttack { "Attack", " s" };
    SliderControl filterDecay { "Decay", " s" };
    SliderControl filterSustain { "Sustain", {}, false, true };
    SliderControl filterRelease { "Release", " s" };
    EnvelopeDisplayComponent filterEnvelopeDisplay { filterAttack.slider, filterDecay.slider,
                                                     filterSustain.slider, filterRelease.slider };
    SliderControl unisonVoices { "Voices", "" };
    SliderControl unisonDetune { "Detune", " ct" };
    SliderControl stereoSpread { "Spread", {}, false, true };
    ToggleControl transientEnabled { "On" };
    SliderControl transientLevel { "Level", " dB" };
    SliderControl transientDecay { "Decay", " s" };
    SliderControl transientTone { "Tone", {}, false, true };
    ToggleControl noiseEnabled { "On" };
    SliderControl noiseLevel { "Level", " dB" };
    SliderControl noiseDecay { "Decay", " s" };
    SliderControl noiseTone { "Tone", {}, false, true };

    std::array<std::unique_ptr<SliderAttachment>, 22> sliderAttachments;
    std::array<std::unique_ptr<ButtonAttachment>, 3> buttonAttachments;
};

class SettingsPageComponent final : public juce::Component
{
public:
    explicit SettingsPageComponent (DeepPluckGuiPreferences& preferences);
    void paint (juce::Graphics&) override;
    void resized() override;
    void refresh();
    std::function<void (bool)> onHoverHelpChanged;
    std::function<void()> onWelcomeRequested;

private:
    DeepPluckGuiPreferences& preferences;
    juce::GroupComponent preferencesPanel { "settings.preferences.panel", "PREFERENCES" };
    juce::ToggleButton hoverHelp { "Hover Help" };
    juce::ToggleButton showWelcome { "Welcome Screen" };
    juce::TextButton showWelcomeNow { "Show Welcome Now" };
    juce::GroupComponent aboutPanel { "settings.about.panel", "ABOUT" };
    juce::Label aboutProduct;
    juce::Label aboutVersion;
    juce::Label aboutFormats;
    juce::Label aboutPlatform;
    juce::Label aboutJuce;
    juce::Label aboutDescription;
    juce::Label aboutCredits;
    juce::HyperlinkButton aboutWebsite {
        "www.tonetrax-music.de", juce::URL { "https://www.tonetrax-music.de" }
    };
};

class WelcomeOverlayComponent final : public juce::Component
{
public:
    explicit WelcomeOverlayComponent (DeepPluckGuiPreferences& preferences);
    void paint (juce::Graphics&) override;
    void resized() override;
    void showIfNeeded();
    void showNow();

private:
    void dismiss();
    DeepPluckGuiPreferences& preferences;
    juce::Label title;
    juce::Label subtitle;
    juce::Label orientation;
    juce::Label hint;
    juce::Label credits;
    juce::TextButton continueButton { "Start Playing" };
    juce::ToggleButton dontShowButton { "Don't show this again" };
};

class FooterComponent final : public juce::Component
{
public:
    FooterComponent();
    void paint (juce::Graphics&) override;
};

class PageNavigationComponent final : public juce::Component
{
public:
    PageNavigationComponent();
    void resized() override;
    void setSelectedPage (Page page);
    [[nodiscard]] bool isModulationEnabled() const noexcept;
    [[nodiscard]] bool isEffectsEnabled() const noexcept;
    std::function<void (Page)> onPageSelected;

private:
    juce::TextButton soundButton { "SOUND" };
    juce::TextButton filterButton { "FILTER" };
    juce::TextButton shapeButton { "SHAPE" };
    juce::TextButton modulationButton { "MODULATION" };
    juce::TextButton effectsButton { "FX" };
};

class SoundPageComponent final : public juce::Component
{
public:
    explicit SoundPageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 12;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
    ModulePanelComponent oscillator1Panel { "OSCILLATOR 1" };
    ModulePanelComponent oscillator2Panel { "OSCILLATOR 2" };
    ModulePanelComponent unisonPanel { "UNISON & WIDTH" };
    ChoiceControl oscillator1Waveform { "Waveform" };
    SliderControl oscillator1PulseWidth { "Pulse Width", {}, true };
    ToggleControl oscillator2Enabled { "On / Off" };
    SliderControl oscillator2Level { "Level", " dB" };
    SliderControl oscillator2Octave { "Octave", " oct" };
    SliderControl oscillator2Semitone { "Semitone", " st" };
    SliderControl oscillator2FineTune { "Fine Tune", " ct" };
    ChoiceControl oscillator2Waveform { "Waveform" };
    SliderControl oscillator2PulseWidth { "Pulse Width", {}, true };
    SliderControl unisonVoices { "Voices", " voices" };
    SliderControl unisonDetune { "Detune", " ct" };
    SliderControl stereoSpread { "Stereo Spread", {}, true };
    std::unique_ptr<ComboBoxAttachment> oscillator1WaveformAttachment;
    std::unique_ptr<SliderAttachment> oscillator1PulseWidthAttachment;
    std::unique_ptr<ButtonAttachment> oscillator2EnabledAttachment;
    std::unique_ptr<SliderAttachment> oscillator2LevelAttachment;
    std::unique_ptr<SliderAttachment> oscillator2OctaveAttachment;
    std::unique_ptr<SliderAttachment> oscillator2SemitoneAttachment;
    std::unique_ptr<SliderAttachment> oscillator2FineTuneAttachment;
    std::unique_ptr<ComboBoxAttachment> oscillator2WaveformAttachment;
    std::unique_ptr<SliderAttachment> oscillator2PulseWidthAttachment;
    std::unique_ptr<SliderAttachment> unisonVoicesAttachment;
    std::unique_ptr<SliderAttachment> unisonDetuneAttachment;
    std::unique_ptr<SliderAttachment> stereoSpreadAttachment;
};

class FilterPageComponent final : public juce::Component
{
public:
    explicit FilterPageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 10;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    ModulePanelComponent filterPanel { "FILTER" };
    ModulePanelComponent envelopePanel { "FILTER ENVELOPE" };
    ToggleControl enabled { "On / Off" };
    SliderControl cutoff { "Cutoff", " Hz" };
    SliderControl resonance { "Resonance", {}, true };
    SliderControl drive { "Drive", " dB" };
    SliderControl keyTracking { "Key Track", {}, true };
    SliderControl envAmount { "Env Amount", " oct" };
    SliderControl attack { "Attack", " s" };
    SliderControl decay { "Decay", " s" };
    SliderControl sustain { "Sustain", {}, true };
    SliderControl release { "Release", " s" };
    std::unique_ptr<ButtonAttachment> enabledAttachment;
    std::unique_ptr<SliderAttachment> cutoffAttachment;
    std::unique_ptr<SliderAttachment> resonanceAttachment;
    std::unique_ptr<SliderAttachment> driveAttachment;
    std::unique_ptr<SliderAttachment> keyTrackingAttachment;
    std::unique_ptr<SliderAttachment> envAmountAttachment;
    std::unique_ptr<SliderAttachment> attackAttachment;
    std::unique_ptr<SliderAttachment> decayAttachment;
    std::unique_ptr<SliderAttachment> sustainAttachment;
    std::unique_ptr<SliderAttachment> releaseAttachment;
};

class ShapePageComponent final : public juce::Component
{
public:
    explicit ShapePageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 12;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    ModulePanelComponent ampPanel { "AMP ENVELOPE" };
    ModulePanelComponent transientPanel { "TRANSIENT ENGINE" };
    ModulePanelComponent noisePanel { "NOISE ENGINE" };
    SliderControl ampAttack { "Attack", " s" };
    SliderControl ampDecay { "Decay", " s" };
    SliderControl ampSustain { "Sustain", {}, true };
    SliderControl ampRelease { "Release", " s" };
    ToggleControl transientEnabled { "On / Off" };
    SliderControl transientLevel { "Level", " dB" };
    SliderControl transientDecay { "Decay", " s" };
    SliderControl transientTone { "Tone", {}, true };
    ToggleControl noiseEnabled { "On / Off" };
    SliderControl noiseLevel { "Level", " dB" };
    SliderControl noiseDecay { "Decay", " s" };
    SliderControl noiseTone { "Tone", {}, true };
    std::unique_ptr<SliderAttachment> ampAttackAttachment;
    std::unique_ptr<SliderAttachment> ampDecayAttachment;
    std::unique_ptr<SliderAttachment> ampSustainAttachment;
    std::unique_ptr<SliderAttachment> ampReleaseAttachment;
    std::unique_ptr<ButtonAttachment> transientEnabledAttachment;
    std::unique_ptr<SliderAttachment> transientLevelAttachment;
    std::unique_ptr<SliderAttachment> transientDecayAttachment;
    std::unique_ptr<SliderAttachment> transientToneAttachment;
    std::unique_ptr<ButtonAttachment> noiseEnabledAttachment;
    std::unique_ptr<SliderAttachment> noiseLevelAttachment;
    std::unique_ptr<SliderAttachment> noiseDecayAttachment;
    std::unique_ptr<SliderAttachment> noiseToneAttachment;
};

class ModulationPageComponent final : public juce::Component
{
public:
    explicit ModulationPageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 8;

private:
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
    using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
    using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
    ModulePanelComponent lfoPanel { "LFO" };
    ModulePanelComponent amountsPanel { "AMOUNTS" };
    ToggleControl sync { "Free / Sync" };
    SliderControl rate { "Free Rate", " Hz" };
    ChoiceControl syncDivision { "Sync Division" };
    ChoiceControl waveform { "Waveform" };
    ChoiceControl restartMode { "Restart" };
    SliderControl filterAmount { "Filter", " oct" };
    SliderControl pitchAmount { "Pitch", " ct" };
    SliderControl spreadAmount { "Spread", {}, false, true };
    std::unique_ptr<ButtonAttachment> syncAttachment;
    std::unique_ptr<SliderAttachment> rateAttachment;
    std::unique_ptr<ComboBoxAttachment> syncDivisionAttachment;
    std::unique_ptr<ComboBoxAttachment> waveformAttachment;
    std::unique_ptr<ComboBoxAttachment> restartModeAttachment;
    std::unique_ptr<SliderAttachment> filterAmountAttachment;
    std::unique_ptr<SliderAttachment> pitchAmountAttachment;
    std::unique_ptr<SliderAttachment> spreadAmountAttachment;
};

class FxOrderController final : private juce::AudioProcessorValueTreeState::Listener
{
public:
    FxOrderController (juce::AudioProcessorValueTreeState& state,
                       juce::ComboBox& chorusPosition,
                       juce::ComboBox& delayPosition,
                       juce::ComboBox& reverbPosition);
    ~FxOrderController() override;

    void synchronizeFromParameter();
    void resetOrder();

private:
    void parameterChanged (const juce::String&, float newValue) override;
    void positionChanged (EffectKind effect, const juce::ComboBox& control);
    void publishOrder (int order);
    void updateControls (int order);

    juce::AudioProcessorValueTreeState& state;
    juce::ComboBox& chorus;
    juce::ComboBox& delay;
    juce::ComboBox& reverb;
    std::atomic<int> latestOrder { 0 };
    bool updatingControls = false;
};

class EffectsPageComponent final : public juce::Component
{
public:
    explicit EffectsPageComponent (DeepPluckAudioProcessor& processor);
    void resized() override;
    void refreshEnabledState();
    static constexpr int attachmentCount = 18;

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

    DeepPluckAudioProcessor& processor;
    ModulePanelComponent chorusPanel { "CHORUS" };
    ModulePanelComponent delayPanel { "DELAY" };
    ModulePanelComponent reverbPanel { "REVERB" };
    juce::GroupComponent utilityPanel { "fx.utilities", "FX RACK" };
    juce::TextButton rackRandom { "Random" };
    juce::ToggleButton rackLock { "Lock" };
    juce::TextButton resetOrder { "Reset Order" };

    ToggleControl chorusBypass { "Bypass" };
    ChoiceControl chorusPosition { "Position" };
    SliderControl chorusMix { "Mix", {}, true };
    SliderControl chorusRate { "Rate", " Hz" };
    SliderControl chorusDepth { "Depth", {}, true };
    SliderControl chorusWidth { "Width", {}, true };

    ToggleControl delayBypass { "Bypass" };
    ChoiceControl delayPosition { "Position" };
    SliderControl delayMix { "Mix", {}, true };
    ToggleControl delaySync { "Free / Sync" };
    SliderControl delayTime { "Free Time", " s" };
    ChoiceControl delayDivision { "Division" };
    SliderControl delayFeedback { "Feedback", {}, true };
    SliderControl delayTone { "Tone", " Hz" };

    ToggleControl reverbBypass { "Bypass" };
    ChoiceControl reverbPosition { "Position" };
    SliderControl reverbMix { "Mix", {}, true };
    SliderControl reverbSize { "Size", {}, true };
    SliderControl reverbPreDelay { "Pre-Delay", " s" };
    SliderControl reverbDamping { "Damping", {}, true };

    std::unique_ptr<ButtonAttachment> chorusBypassAttachment;
    std::unique_ptr<SliderAttachment> chorusRateAttachment;
    std::unique_ptr<SliderAttachment> chorusDepthAttachment;
    std::unique_ptr<SliderAttachment> chorusWidthAttachment;
    std::unique_ptr<SliderAttachment> chorusMixAttachment;
    std::unique_ptr<ButtonAttachment> delayBypassAttachment;
    std::unique_ptr<ButtonAttachment> delaySyncAttachment;
    std::unique_ptr<SliderAttachment> delayTimeAttachment;
    std::unique_ptr<ComboBoxAttachment> delayDivisionAttachment;
    std::unique_ptr<SliderAttachment> delayFeedbackAttachment;
    std::unique_ptr<SliderAttachment> delayToneAttachment;
    std::unique_ptr<SliderAttachment> delayMixAttachment;
    std::unique_ptr<ButtonAttachment> reverbBypassAttachment;
    std::unique_ptr<SliderAttachment> reverbSizeAttachment;
    std::unique_ptr<SliderAttachment> reverbPreDelayAttachment;
    std::unique_ptr<SliderAttachment> reverbDampingAttachment;
    std::unique_ptr<SliderAttachment> reverbMixAttachment;
    std::unique_ptr<FxOrderController> orderController;
};
}
