#pragma once

#include <juce_gui_basics/juce_gui_basics.h>

#include <array>
#include <atomic>
#include <memory>

namespace deeppluck::gui
{
namespace Colours
{
inline const auto background = juce::Colour::fromRGB (3, 10, 12);
inline const auto surface = juce::Colour::fromRGB (5, 19, 22);
inline const auto surfaceRaised = juce::Colour::fromRGB (8, 27, 30);
inline const auto border = juce::Colour::fromRGB (28, 62, 65);
inline const auto teal = juce::Colour::fromRGB (36, 220, 226);
inline const auto tealMuted = juce::Colour::fromRGB (38, 130, 137);
inline const auto amber = juce::Colour::fromRGB (255, 139, 23);
inline const auto amberSoft = juce::Colour::fromRGB (226, 164, 76);
inline const auto text = juce::Colour::fromRGB (227, 232, 231);
inline const auto textMuted = juce::Colour::fromRGB (132, 143, 143);
inline constexpr auto rotaryDiameterProperty = "deeppluck.rotaryDiameter";
}

juce::Path createWaveformPath (int waveformIndex, float pulseWidth,
                               juce::Rectangle<float> bounds);

struct EnvelopeDisplayGeometry
{
    float attackX = 0.0f;
    float decayX = 0.0f;
    float sustainX = 0.0f;
    float releaseX = 0.0f;
    float sustainY = 0.0f;
};

EnvelopeDisplayGeometry calculateEnvelopeDisplayGeometry (
    float attack, float decay, float sustain, float release,
    juce::Rectangle<float> bounds);
juce::Path createEnvelopePath (float attack, float decay, float sustain, float release,
                               juce::Rectangle<float> bounds);
float normaliseMeterDecibels (float decibels) noexcept;

enum class WorkflowIconKind
{
    none,
    dice,
    lock,
    undo,
    save,
    deleteAction,
    refresh,
    folder,
    previous,
    next
};

WorkflowIconKind getWorkflowIconKind (const juce::String& componentId);

class DeepPluckLookAndFeel final : public juce::LookAndFeel_V4
{
public:
    DeepPluckLookAndFeel();

    void drawRotarySlider (juce::Graphics&, int x, int y, int width, int height,
                           float sliderPosition, float rotaryStartAngle,
                           float rotaryEndAngle, juce::Slider&) override;
    void drawButtonBackground (juce::Graphics&, juce::Button&, const juce::Colour&,
                               bool highlighted, bool down) override;
    void drawButtonText (juce::Graphics&, juce::TextButton&, bool highlighted,
                         bool down) override;
    void drawToggleButton (juce::Graphics&, juce::ToggleButton&, bool highlighted,
                           bool down) override;
    void drawComboBox (juce::Graphics&, int width, int height, bool down,
                       int buttonX, int buttonY, int buttonW, int buttonH,
                       juce::ComboBox&) override;
    void drawPopupMenuBackground (juce::Graphics&, int width, int height) override;
    void drawTooltip (juce::Graphics&, const juce::String&, int width, int height) override;
    juce::Slider::SliderLayout getSliderLayout (juce::Slider&) override;

private:
    static void drawIcon (juce::Graphics&, const juce::String& componentId,
                          juce::Rectangle<float> bounds, juce::Colour colour);
};

class DeepPluckTooltipWindow final : public juce::TooltipWindow
{
public:
    explicit DeepPluckTooltipWindow (juce::Component* parent);
    void setEnabled (bool shouldBeEnabled);
    juce::String getTipFor (juce::Component&) override;

private:
    bool enabled = true;
};

struct GuiPreferenceState
{
    bool hoverHelp = true;
    bool welcomeEnabled = true;
    bool welcomeSeen = false;
};

class DeepPluckGuiPreferences final
{
public:
    static std::shared_ptr<DeepPluckGuiPreferences> shared();
    static std::shared_ptr<DeepPluckGuiPreferences> createForTests (const juce::File& file);
    static void installSharedForTests (std::shared_ptr<DeepPluckGuiPreferences> preferences);
    static void resetSharedForTests();

    [[nodiscard]] GuiPreferenceState get() const;
    void setHoverHelp (bool enabled);
    void setWelcomeEnabled (bool enabled);
    void markWelcomeSeen();
    void requestWelcomeAgain();
    [[nodiscard]] bool shouldShowWelcome() const;
    [[nodiscard]] juce::File getStorageFile() const;

private:
    explicit DeepPluckGuiPreferences (juce::File storageFile);
    void load();
    void saveLocked();

    juce::File file;
    mutable juce::CriticalSection mutex;
    GuiPreferenceState state;
};

class WaveformDisplayComponent final : public juce::Component
{
public:
    WaveformDisplayComponent (juce::ComboBox& waveformControl,
                              juce::Slider& pulseWidthControl);
    void paint (juce::Graphics&) override;

private:
    juce::ComboBox& waveform;
    juce::Slider& pulseWidth;
};

class EnvelopeDisplayComponent final : public juce::Component
{
public:
    EnvelopeDisplayComponent (juce::Slider& attackControl, juce::Slider& decayControl,
                              juce::Slider& sustainControl, juce::Slider& releaseControl);
    void paint (juce::Graphics&) override;

private:
    juce::Slider& attack;
    juce::Slider& decay;
    juce::Slider& sustain;
    juce::Slider& release;
};

class MeterBarComponent final : public juce::Component
{
public:
    explicit MeterBarComponent (const juce::String& channelName);
    void setLevel (float newLevel);
    [[nodiscard]] float getDisplayedLevel() const noexcept;
    void paint (juce::Graphics&) override;

private:
    juce::String name;
    float level = 0.0f;
};

class StereoPeakMeterComponent final : public juce::Component
{
public:
    StereoPeakMeterComponent();
    void setLevels (float left, float right);
    [[nodiscard]] std::array<float, 2> getDisplayedLevels() const noexcept;
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    MeterBarComponent left { "L" };
    MeterBarComponent right { "R" };
};
}
