#ifndef AXIOMDESIGNSYSTEM_H
#define AXIOMDESIGNSYSTEM_H

#pragma once
#include <corecrt_math_defines.h>

#include "JuceLibraryCode/BinaryData.h"


namespace AXIOM {
    class DesignSystem : public juce::LookAndFeel_V4 {
    public:
        DesignSystem();

        ~DesignSystem() override = default;

        struct Colours {
            //=====================BASE-COLORS========================//
            static inline const juce::Colour ACCENTCOLOUR = juce::Colour::fromRGB(86, 254, 255); // #56FEFF
            static inline const juce::Colour BACKGROUNDCOLOUR = juce::Colour::fromRGB(51, 51, 51); // #333333
            static inline const juce::Colour FOREGROUNDCOLOUR = juce::Colour::fromRGB(252, 250, 249); // #FCFAF9
            static inline const juce::Colour SURFACECOLOUR = juce::Colour::fromRGB(31, 31, 31); // #1F1F1F
            static inline const juce::Colour MUTEDTEXTCOLOUR = juce::Colour::fromRGB(207, 207, 207); // #CFCFCF
            static inline const juce::Colour ACCENTWEAKCOLOUR = juce::Colour::fromRGB(61, 183, 183); // #3DB7B7

            //===================== HOVER COLORS ====================//
            static inline const juce::Colour ACCENTHOVER        = juce::Colour::fromRGB(140, 255, 255);  // #8CFFFF
            static inline const juce::Colour BACKGROUNDHOVER    = juce::Colour::fromRGB(70, 70, 70);     // #464646
            static inline const juce::Colour FOREGROUNDHOVER    = juce::Colour::fromRGB(255, 255, 255);  // #FFFFFF
            static inline const juce::Colour SURFACEHOVER       = juce::Colour::fromRGB(48, 48, 48);     // #303030
            static inline const juce::Colour MUTEDTEXTHOVER     = juce::Colour::fromRGB(235, 235, 235);  // #EBEBEB
            static inline const juce::Colour ACCENTWEAKHOVER    = juce::Colour::fromRGB(100, 225, 225);  // #64E1E1

            //=====================BYPASS-COLORS========================//
            static inline const juce::Colour ACCENTBYPASS = juce::Colour::fromRGB(110, 255, 255); // #6EFFFF
            static inline const juce::Colour BACKGROUNDBYPASS = juce::Colour::fromRGB(66, 66, 66); // #424242
            static inline const juce::Colour FOREGROUNDBYPASS = juce::Colour::fromRGB(255, 255, 255); // #FFFFFF
            static inline const juce::Colour SURFACEBYPASS = juce::Colour::fromRGB(42, 42, 42); // #2A2A2A
            static inline const juce::Colour MUTEDTEXTBYPASS = juce::Colour::fromRGB(230, 230, 230); // #E6E6E6
            static inline const juce::Colour ACCENTWEAKBYPASS = juce::Colour::fromRGB(82, 210, 210); // #52D2D2

        };

        struct Spacing {
            enum class DensityMode {
                Narrow,
                Compact,
                Regular,
                Wide,
                SuperWide
            };

            static constexpr float getDensityFactor(DensityMode mode) {
                switch (mode) {
                    case DensityMode::Narrow: return {0.9f};

                    case DensityMode::Compact: return {0.95f};

                    case DensityMode::Regular: return {1.0f};

                    case DensityMode::Wide: return {1.08f};

                    case DensityMode::SuperWide: return {1.15f};
                }
                return {1.0f};
            };

            enum class SizeMode {
                XS,
                S,
                M,
                L,
                XL
            };

            static constexpr int getSizeUnits(SizeMode size) {
                switch (size) {
                    case SizeMode::XS: return 1;
                    case SizeMode::S: return 2;
                    case SizeMode::M: return 3;
                    case SizeMode::L: return 4;
                    case SizeMode::XL: return 6;
                }
                return 1;
            };

            enum class SnapMode {
                Nearest, Floor, Ceiling
            };

            enum class uiScaleMode {
                XS, S, M, L, XL
            };

            struct Scale {
                float baseUnit = 8.0f;
                float densityFactor = 1.0f;
                float uiScale = 1.0f;
                float uiScaleInfluence = 0.3f;
                static constexpr float MINCLAMP = 4.0f;
                static constexpr float MAXCLAMP = 56.0f;

                SnapMode snapMode = SnapMode::Nearest;
                float stepUnits = 1.0f;


                float space(SizeMode size) noexcept {
                    float px;
                    auto units = static_cast<float>(getSizeUnits(size));

                    px = units * baseUnit;
                    px *= densityFactor;
                    px *= 1.0f + (uiScale - 1.0f) * uiScaleInfluence;
                    px = snapToGrid(px, stepUnits, snapMode);
                    return std::clamp(px, MINCLAMP, MAXCLAMP);
                }

                float snapToGrid(float px, float stepUnits, SnapMode mode) noexcept {
                    float grid = baseUnit * stepUnits;
                    if (grid <= 0.0f) return px;

                    float units = px / grid;
                    float snappedUnits;
                    switch (mode) {
                        case SnapMode::Nearest: snappedUnits = std::round(units);
                        break;
                        case SnapMode::Floor: snappedUnits = std::floor(units + 1e-6f);
                        break;
                        case SnapMode::Ceiling: snappedUnits = std::ceil(units - 1e-6f);
                        break;
                    }
                    return snappedUnits * grid;
                };
            };

            inline static Scale scale;

            static void setDensity(DensityMode mode) noexcept {
                scale.densityFactor = getDensityFactor(mode);
            }

            static void setUiScale(uiScaleMode mode) noexcept {
                switch (mode) {
                    case uiScaleMode::XS: scale.uiScale = 0.5f;
                    break;
                    case uiScaleMode::S: scale.uiScale = 0.75f;
                    break;
                    case uiScaleMode::M: scale.uiScale = 1.0f;
                    break;
                    case uiScaleMode::L: scale.uiScale = 1.5f;
                    break;
                    case uiScaleMode::XL: scale.uiScale = 1.75f;
                    break;
                }
                scale.uiScale = juce::jlimit(0.3f, 3.0f, scale.uiScale);
            }
        };

        struct Typography {
            inline static juce::Typeface::Ptr orbitronFont =
                    juce::Typeface::createSystemTypefaceFor(
                        BinaryData::OrbitronRegular_ttf,
                        BinaryData::OrbitronRegular_ttfSize);
            inline static juce::Typeface::Ptr horizonFont =
                    juce::Typeface::createSystemTypefaceFor(
                        BinaryData::horizon_otf,
                        BinaryData::horizon_otfSize);
            inline static juce::Typeface::Ptr jetBrainsMonoFont =
                    juce::Typeface::createSystemTypefaceFor(
                        BinaryData::JetBrainsMonoRegular_ttf,
                        BinaryData::JetBrainsMonoRegular_ttfSize);


            inline static const juce::String primaryFamily{"Orbitron Bold"};
            inline static const juce::String displayFamily{"Horizon"};
            inline static const juce::String monoFamily{"JetBrains Mono"};

            struct Scale {
                float basePx = 14.0f;
                float ratio = 1.125f;

                float sizeFor(int step) const noexcept {
                    // step = 0 => basePx, step = 1 => base*ratio, step = -1 => base/ratio, ...
                    return basePx * std::pow(ratio, static_cast<float>(step));
                }
            };

            inline static Scale scale{};

            enum class Style {
                Display,
                H1, H2, H3,
                Subtitle,
                Body,
                Small,
                Caption,
                Button,
                Mono,
                Overline
            };

            struct TextToken {
                juce::String family;
                int stepFromBase;
                bool bold = false;
                bool italic = false;
                bool uppercase = false;
                float letterSpacing = 0.0f;
                float lineHeight = 1.25f;
                bool useMonospace = false;
            };

            static TextToken getToken(Style style) noexcept {
                switch (style) {
                    case Style::Display: return {displayFamily, 5, true, false, false, 0.0f, 1.15f, false};
                    case Style::H1: return {primaryFamily, 4, true, false, false, 0.0f, 1.15f, false};
                    case Style::H2: return {primaryFamily, 3, true, false, false, 0.0f, 1.20f, false};
                    case Style::H3: return {primaryFamily, 2, false, false, false, 0.0f, 1.20f, false};
                    case Style::Subtitle: return {primaryFamily, 1, false, false, false, 0.01f, 1.25f, false};
                    case Style::Body: return {primaryFamily, 0, false, false, false, 0.0f, 1.35f, false};
                    case Style::Small: return {primaryFamily, -1, false, false, false, 0.0f, 1.35f, false};
                    case Style::Caption: return {primaryFamily, -2, false, false, false, 0.02f, 1.40f, false};
                    case Style::Button: return {primaryFamily, 0, true, false, false, 0.02f, 1.10f, false};
                    case Style::Mono: return {monoFamily, -1, false, false, false, 0.0f, 1.30f, true};
                    case Style::Overline: return {primaryFamily, -3, true, false, true, 0.06f, 1.20f, false};
                }
                return {primaryFamily, 0, false, false, false, 0.0f, 1.3f, false};
            }

            static juce::Font getFont(Style style, float uiScale = Spacing::scale.uiScale) {
                const auto t = getToken(style);
                const auto fam = t.family;
                auto height = scale.sizeFor(t.stepFromBase) * uiScale;


                juce::Font f{fam, height, juce::Font::plain};

                if (fam == displayFamily && horizonFont != nullptr) {
                    f = juce::Font(horizonFont).withHeight(height);
                } else if (fam == primaryFamily && orbitronFont != nullptr) {
                    f = juce::Font(orbitronFont).withHeight(height);
                } else if (fam == monoFamily && jetBrainsMonoFont != nullptr) {
                    f = juce::Font(jetBrainsMonoFont).withHeight(height);
                } else
                    f.setTypefaceName(fam);

                f.setBold(t.bold);
                f.setItalic(t.italic);
                f.setExtraKerningFactor(t.letterSpacing);

                return f;
            }

            static void applyToLabel(juce::Label &label, Style style, float uiScale = Spacing::scale.uiScale) {
                label.setFont(getFont(style, uiScale));

                if (getToken(style).uppercase)
                    label.setText(label.getText().toUpperCase(), juce::NotificationType::dontSendNotification);

                label.setMinimumHorizontalScale(1.0f);
            }

            static juce::TextLayout createTextLayout(juce::String text,
                                                     juce::Rectangle<float> bounds,
                                                     Style style,
                                                     juce::Colour colour,
                                                     float uiScale = Spacing::scale.uiScale,
                                                     juce::Justification just = juce::Justification::topLeft) {
                const auto token = getToken(style);
                const auto font = getFont(style, uiScale);

                juce::AttributedString as;
                as.setJustification(just);

                if (token.uppercase)
                    text = text.toUpperCase();

                as.append(text, font, colour);

                juce::TextLayout tl;
                tl.createLayout(as, bounds.getWidth());
                return tl;
            }
        };

        struct Shape {
            enum class RadiusMode {
                XS,
                S,
                M,
                L,
                XL
            };
            enum class StrokeMode {
                Hairline,
                Thin,
                Regular,
                Bold
            };
            enum class CornerStyle {
                Rounded,
                Cut,
                Squircle
            };

            struct Scale {
                float baseRadius = 8.0f;
                float uiScale = Spacing::scale.uiScale;
                float uiScaleInfluenceRadius = 0.5f;
                float uiScaleInfluenceStroke = 0.3f;
                static constexpr float MINRADIUS = 2.0f;
                static constexpr float MAXRADIUS = 28.0f;
                static constexpr float MINSTROKE = 1.0f;
                static constexpr float MAXSTROKE = 3.0f;
            };
            inline static Scale scale{};

            static constexpr int getRadiusUnits(RadiusMode mode) {
                switch (mode) {
                    case RadiusMode::XS: return 1;
                    case RadiusMode::S: return 2;
                    case RadiusMode::M: return 3;
                    case RadiusMode::L: return 4;
                    case RadiusMode::XL: return 6;
                }
                return 1;
            };

            static float getRadius(RadiusMode mode, juce::Rectangle<float> bounds) {
                int units = getRadiusUnits(mode);

                float maxRadiusAllowed = std::min(bounds.getHeight(), bounds.getWidth()) * 0.5f;

                float radius = units * scale.baseRadius;

                radius *= (1.0f + (Spacing::scale.uiScale - 1.0f) * scale.uiScaleInfluenceRadius);

                radius = std::round(radius * 2.0f) / 2.0f;

                radius = juce::jlimit(scale.MINRADIUS, std::min(scale.MAXRADIUS, maxRadiusAllowed), radius);

                return radius;
            };

            static constexpr float getStrokeUnits(StrokeMode mode) {
                switch (mode) {
                    case StrokeMode::Hairline: return 0.5f;
                    case StrokeMode::Thin: return 1.0f;
                    case StrokeMode::Regular: return 1.5f;
                    case StrokeMode::Bold: return 2.0f;
                }
                return 1.0f;
            };

            static float getStrokeWidth(StrokeMode mode) {
                float width = getStrokeUnits(mode);
                width *= 1.0f + (Spacing::scale.uiScale - 1) * scale.uiScaleInfluenceStroke;
                width = std::round(width * 2.0f) / 2.0f;
                width = juce::jlimit(scale.MINSTROKE, scale.MAXSTROKE, width);
                return width;
            };

        };

        struct Shadows {
        };

        struct Opacity {
            static constexpr float MINALPHA = 0.04f;
            static constexpr float MAXALPHA = 0.95f;
            static constexpr float MUTEDMUL = 0.5f;
            struct AlphaToken {
                enum class Role {
                    Text, Icon, Surface, Overlay, Stroke, FocusGlow, Disabled, InteractiveFill
                };
                enum class State {
                    Rest, Hover, Active, Checked, Dragged, Disabled
                };
                enum class Emphasis {
                    High, Medium, Low, Muted
                };
                Role role;
                State state;
                Emphasis emphasis;
            };

            static AlphaToken getAlphaToken(AlphaToken::Role role, AlphaToken::State state, AlphaToken::Emphasis emphasis) {
                return AlphaToken{ role, state, emphasis };
            };

            static float getAlphaFactor(AlphaToken::Role role, AlphaToken::State state, AlphaToken::Emphasis emphasis) {
                float alphaFactor;
                if (role == AlphaToken::Role::Text || role == AlphaToken::Role::Icon) {
                    alphaFactor = fromEmphasis(emphasis);
                }else {
                    alphaFactor = fromRole(role);
                }
                alphaFactor += fromState(state);
                if (state == AlphaToken::State::Disabled) alphaFactor *= MUTEDMUL;
                alphaFactor = std::clamp(alphaFactor, MINALPHA, MAXALPHA);
                return alphaFactor;
            }

            static float fromEmphasis(AlphaToken::Emphasis emphasis) {
                switch (emphasis) {
                    case AlphaToken::Emphasis::High:   return 0.92f;
                    case AlphaToken::Emphasis::Medium: return 0.72f;
                    case AlphaToken::Emphasis::Low:    return 0.56f;
                    case AlphaToken::Emphasis::Muted:  return 0.42f;
                }
                return 0.92f;
            }

            static float fromRole(AlphaToken::Role role) {
                switch (role) {
                    case AlphaToken::Role::Surface:         return 0.96f;
                    case AlphaToken::Role::Overlay:         return 0.28f;
                    case AlphaToken::Role::Stroke:          return 0.14f;
                    case AlphaToken::Role::FocusGlow:       return 0.18f;
                    case AlphaToken::Role::Disabled:        return 0.45f;
                    case AlphaToken::Role::InteractiveFill: return 0.95f;
                    default:                                return 1.0f;
                }
            }

            static float fromState(AlphaToken::State state) {
                switch (state) {
                    case AlphaToken::State::Rest: return 0.0f;
                    case AlphaToken::State::Hover: return 0.06f;
                    case AlphaToken::State::Active: return 0.10f;
                    case AlphaToken::State::Checked: return 0.08f;
                    case AlphaToken::State::Dragged: return 0.10f;
                    case AlphaToken::State::Disabled: return 0.0f;
                }
                return 0.0f;
            }

            static juce::Colour applyAlpha(juce::Colour baseColour, AlphaToken token) {
                float alphaFactor = getAlphaFactor(token.role, token.state, token.emphasis);
                return baseColour.withMultipliedAlpha(alphaFactor);
            }
        };

        /*struct Motion {
            enum class Duration {
                Slowest,
                Slow,
                Moderate,
                Regular,
                Fast
            };
            enum class Ease {
                Standard,
                Decel,
                Accel,
                Emphasized,
                Overshoot
            };
            enum class Spring{
                Snappy,
                Natural,
                Gentle,
                SoftOvershoot
            };
            enum class MotionRole {
                MenuEnter, MenuExit, ModalEnter, TooltipEnter, TooltipExit
            };
            enum class MotionMicro {
                Hover, Pressed, Focus, Success, Error
            };
            enum class StaggerProfile {
                Forward, CenterOut, Reverse
            };

            struct CubicBezier {
                float x1, y1, x2, y2;
            };
            struct SpringParameters {
                float tension, friction, damping, mass;
            };
            struct MicroParameters {
                float scaleDelta, opacityDelta;
                Duration duration;
                Ease ease;
            };

            static float getDuration(Duration duration) {
                switch (duration) {
                    case Duration::Slowest: return 0.36f;
                    case Duration::Slow: return 0.28f;
                    case Duration::Moderate: return 0.2f;
                    case Duration::Regular: return 0.14f;
                    case Duration::Fast: return 0.09f;
                }
                return 0.2f;
            }
            static CubicBezier getEasing(Ease easing) {
                switch (easing) {
                    case Ease::Standard: return {0.2f, 0.0f, 0.0f, 1.0f};
                    case Ease::Decel: return {0.05f, 0.7f, 0.1f, 1.0f};
                    case Ease::Accel: return {0.3f, 0.0f, 0.8f, 0.15f};
                    case Ease::Emphasized: return {0.2f, 0.0f, 0.0f, 1.0f};
                    default: return {0.2f, 0.0f, 0.0f, 1.0f};;
                    // EIGENES STRUCT SPRING case Ease::Overshoot: return {0.2f, 0.0f, 0.0f, 1.0f};
                }
            }
            static SpringParameters getSpringPreset(Spring mode) {
                switch (mode) {
                    case Spring::Snappy: return {220.0f, 26.0f, 0.6f, 1.0f};
                    case Spring::Natural: return {170.0f, 22.0f, 0.68f, 1.0f};
                    case Spring::Gentle: return {120.0f, 20.0f, 0.75f, 1.0f};
                    case Spring::SoftOvershoot: return {180.0f, 17.0f, 0.55f, 1.0f};
                }
                return {170.0f, 22.0f, 0.68f, 1.0f};
            }
            static MicroParameters getMicroPreset(MotionMicro mode) {
                switch (mode) {
                    case MotionMicro::Hover: return {0.02f, 0.06f, Duration::Fast, Ease::Emphasized};
                    case MotionMicro::Pressed: return {-0.02f, 0.0f, Duration::Fast, Ease::Standard};
                    case MotionMicro::Focus: return {0.0f, 0.02f, Duration::Regular, Ease::Decel};
                    case MotionMicro::Success: return {0.04f, 0.04f, Duration::Regular, Ease::Emphasized};
                    case MotionMicro::Error: return {0.00f, 0.00f, Duration::Fast, Ease::Standard};
                }
                return {0.0f, 0.0f, Duration::Regular, Ease::Standard};
            }

        };*/

        struct Components {

            class BypassButtonLookAndFeel : public juce::LookAndFeel_V4 {
            public:
                BypassButtonLookAndFeel()
                {
                    svgXml = juce::XmlDocument::parse(BinaryData::bypass_icon_svg);}



                void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
                                    bool shouldDrawButtonAsHighlighted,
                                    bool shouldDrawButtonAsDown) override
                {

                    bool isHovered = button.isMouseOverOrDragging();
                    bool isToggled = button.getToggleState();
                    bool isEnabled = button.isEnabled();
                    auto bounds = button.getLocalBounds().toFloat();
                    auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f;
                    auto hoverFactor = isHovered ? 1.05f : 1.0f;

                    auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
                        .withCentre(bounds.getCentre());

                    float bypassOpacity = 1.0f;

                    if (svgXml != nullptr) {
                        // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
                        auto icon = juce::Drawable::createFromSVG(*svgXml);
                        if (icon != nullptr)
                        {

                            juce::Colour colour;
                            if (!isEnabled) {
                                colour = isToggled ? Colours::SURFACEBYPASS : Colours::ACCENTWEAKCOLOUR;
                            }
                            else if (isHovered) {
                                colour = isToggled ? Colours::SURFACEHOVER : Colours::ACCENTHOVER;
                            } else {
                                colour = isToggled
                                ? Colours::SURFACECOLOUR
                                : Colours::ACCENTCOLOUR;
                            }


                            // Icon zeichnen
                            icon->replaceColour(juce::Colours::black, colour);
                            icon->drawWithin(g, iconBounds,
                                juce::RectanglePlacement::centred, bypassOpacity);
                        }
                    }

                }

            private:
                std::unique_ptr<juce::XmlElement> svgXml;
            };

            class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 {
            public:
                PresetMenuButtonLookAndFeel()
                {
                    presetMenuButton = juce::XmlDocument::parse(BinaryData::preset_menu_icon_svg);
                }

                void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
                                    bool shouldDrawButtonAsHighlighted,
                                    bool shouldDrawButtonAsDown) override
                {

                    bool isHovered = button.isMouseOverOrDragging();
                    bool isToggled = button.getToggleState();
                    bool isEnabled = button.isEnabled();
                    auto bounds = button.getLocalBounds().toFloat();
                    auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 1.5f;
                    auto hoverFactor = isHovered ? 1.05f : 1.0f;

                    auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
                    .withCentre(bounds.getCentre());

                    float bypassOpacity = 1.0f;

                    if (presetMenuButton != nullptr) {
                        auto menuIcon = juce::Drawable::createFromSVG(*presetMenuButton);
                        juce::Colour colour;
                        if (isHovered) {
                            colour = isToggled ? Colours::ACCENTHOVER : Colours::BACKGROUNDHOVER;
                        } else {
                            colour = isToggled
                            ? Colours::ACCENTCOLOUR
                            : Colours::BACKGROUNDCOLOUR;
                        }


                        // Icon zeichnen
                        menuIcon->replaceColour(juce::Colours::black, colour);
                        menuIcon->drawWithin(g, iconBounds,
                            juce::RectanglePlacement::centred, 1.0f);
                    }

                }

            private:
                std::unique_ptr<juce::XmlElement> presetMenuButton;
            };

            class SvgToggleButtonLookAndFeel : public juce::LookAndFeel_V4 {
            public:
                SvgToggleButtonLookAndFeel() {
                    passiveSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_passive_icon_svg);
                    activeSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_active_icon_svg);
                };
                float passiveIconOpacity = 1.0f;
                float activeIconOpacity = 0.0f;


                void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
                                    bool shouldDrawButtonAsHighlighted,
                                    bool shouldDrawButtonAsDown) override {


                    const bool isHovered = button.isMouseOverOrDragging();
                    const bool isToggled = button.getToggleState();
                    auto bounds = button.getLocalBounds().toFloat();
                    auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f;

                    auto hoverFactor = isHovered ? 1.05f : 1.0f;

                    auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
                        .withCentre(bounds.getCentre());

                    auto passiveIcon = juce::Drawable::createFromSVG(*passiveSvg);
                    auto activeIcon = juce::Drawable::createFromSVG(*activeSvg);

                    if (passiveIcon != nullptr && activeIcon != nullptr) {

                        activeIcon->drawWithin(g, iconBounds,
                            juce::RectanglePlacement::centred, activeIconOpacity);
                        passiveIcon->drawWithin(g, iconBounds,
                            juce::RectanglePlacement::centred, passiveIconOpacity);

                    }
                }

            private:
                std::unique_ptr<juce::XmlElement> passiveSvg;
                std::unique_ptr<juce::XmlElement> activeSvg;

            };

            class lowBandButtonLookAndFeel : public juce::LookAndFeel_V4 {
            public:
                lowBandButtonLookAndFeel()
                {
                    lowBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg);
                    lowCutIcon = juce::XmlDocument::parse(BinaryData::low_cut_icon_svg);
                    lowBellIcon = juce::XmlDocument::parse(BinaryData::bell_icon_svg);
                    lowShelfIcon = juce::XmlDocument::parse(BinaryData::low_shelf_icon_svg);

                }



                void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
                                    bool shouldDrawButtonAsHighlighted,
                                    bool shouldDrawButtonAsDown) override
                {

                    bool isHovered = button.isMouseOverOrDragging();
                    bool isToggled = button.getToggleState();
                    bool isEnabled = button.isEnabled();
                    auto bounds = button.getLocalBounds().toFloat();
                    auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f;
                    auto hoverFactor = isHovered ? 1.05f : 1.0f;

                    auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
                        .withCentre(bounds.getCentre());

                    float bypassOpacity = 1.0f;

                    if (lowBypassIcon != nullptr && lowCutIcon != nullptr && lowBellIcon != nullptr && lowShelfIcon != nullptr) {
                        // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
                        auto bypassIcon = juce::Drawable::createFromSVG(*lowBypassIcon);
                        auto cutIcon = juce::Drawable::createFromSVG(*lowCutIcon);
                        auto bellIcon = juce::Drawable::createFromSVG(*lowBellIcon);
                        auto shelfIcon = juce::Drawable::createFromSVG(*lowShelfIcon);

                        if (bypassIcon != nullptr && cutIcon != nullptr && bellIcon != nullptr && shelfIcon != nullptr)
                        {

                            juce::Colour colour;
                            if (!isEnabled) {
                                colour = isToggled ? Colours::ACCENTWEAKCOLOUR : Colours::SURFACEBYPASS;
                            }
                            else if (isHovered) {
                                colour = isToggled ? Colours::ACCENTHOVER : Colours::SURFACEHOVER;
                            } else {
                                colour = isToggled
                                ? Colours::ACCENTCOLOUR
                                : Colours::SURFACECOLOUR;
                            }


                            // Icon zeichnen
                            if (button.getName() == "LowBypass") {
                                bypassIcon->replaceColour(juce::Colours::black, colour);
                                bypassIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "LowCut") {
                                cutIcon->replaceColour(juce::Colours::black, colour);
                                cutIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "LowBell") {
                                bellIcon->replaceColour(juce::Colours::black, colour);
                                bellIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "LowShelf") {
                                shelfIcon->replaceColour(juce::Colours::black, colour);
                                shelfIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }

                        }
                    }

                }

            private:
                std::unique_ptr<juce::XmlElement> lowBypassIcon;
                std::unique_ptr<juce::XmlElement> lowCutIcon;
                std::unique_ptr<juce::XmlElement> lowBellIcon;
                std::unique_ptr<juce::XmlElement> lowShelfIcon;
            };

            class highBandButtonLookAndFeel : public juce::LookAndFeel_V4 {
            public:
                highBandButtonLookAndFeel()
                {
                    highBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg);
                    highCutIcon = juce::XmlDocument::parse(BinaryData::high_cut_icon_svg);
                    highBellIcon = juce::XmlDocument::parse(BinaryData::bell_icon_svg);
                    highShelfIcon = juce::XmlDocument::parse(BinaryData::high_shelf_icon_svg);

                }



                void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
                                    bool shouldDrawButtonAsHighlighted,
                                    bool shouldDrawButtonAsDown) override
                {

                    bool isHovered = button.isMouseOverOrDragging();
                    bool isToggled = button.getToggleState();
                    bool isEnabled = button.isEnabled();
                    auto bounds = button.getLocalBounds().toFloat();
                    auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f;
                    auto hoverFactor = isHovered ? 1.05f : 1.0f;

                    auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
                        .withCentre(bounds.getCentre());

                    float bypassOpacity = 1.0f;

                    if (highBypassIcon != nullptr && highCutIcon != nullptr && highBellIcon != nullptr && highShelfIcon != nullptr) {
                        // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
                        auto bypassIcon = juce::Drawable::createFromSVG(*highBypassIcon);
                        auto cutIcon = juce::Drawable::createFromSVG(*highCutIcon);
                        auto bellIcon = juce::Drawable::createFromSVG(*highBellIcon);
                        auto shelfIcon = juce::Drawable::createFromSVG(*highShelfIcon);

                        if (bypassIcon != nullptr && cutIcon != nullptr && bellIcon != nullptr && shelfIcon != nullptr)
                        {

                            juce::Colour colour;
                            if (!isEnabled) {
                                colour = isToggled ? Colours::ACCENTWEAKCOLOUR : Colours::SURFACEBYPASS;
                            }
                            else if (isHovered) {
                                colour = isToggled ? Colours::ACCENTHOVER : Colours::SURFACEHOVER;
                            } else {
                                colour = isToggled
                                ? Colours::ACCENTCOLOUR
                                : Colours::SURFACECOLOUR;
                            }


                            // Icon zeichnen
                            if (button.getName() == "HighBypass") {
                                bypassIcon->replaceColour(juce::Colours::black, colour);
                                bypassIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "HighCut") {
                                cutIcon->replaceColour(juce::Colours::black, colour);
                                cutIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "HighBell") {
                                bellIcon->replaceColour(juce::Colours::black, colour);
                                bellIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                            else if (button.getName() == "HighShelf") {
                                shelfIcon->replaceColour(juce::Colours::black, colour);
                                shelfIcon->drawWithin(g, iconBounds,
                                    juce::RectanglePlacement::centred, bypassOpacity);
                            }
                        }
                    }

                }

            private:
                std::unique_ptr<juce::XmlElement> highBypassIcon;
                std::unique_ptr<juce::XmlElement> highCutIcon;
                std::unique_ptr<juce::XmlElement> highBellIcon;
                std::unique_ptr<juce::XmlElement> highShelfIcon;
            };


            struct ButtonStyles {
            };


            class BaseSliderLookAndFeel : public juce::LookAndFeel_V4
            {
            public:
                juce::Label* createSliderTextBox(juce::Slider& slider) override
                {
                    auto* label = juce::LookAndFeel_V4::createSliderTextBox(slider);

                    label->setColour(juce::Label::backgroundColourId, juce::Colours::transparentBlack);
                    label->setColour(juce::Label::outlineColourId, juce::Colours::transparentBlack);
                    label->setColour(juce::Label::textColourId, Colours::FOREGROUNDCOLOUR);
                    label->setJustificationType(juce::Justification::centred);
                    label->setInterceptsMouseClicks (false, false);
                    Typography::applyToLabel(*label, Typography::Style::Mono, 1.f);
                    return label;
                }
                juce::Slider::SliderLayout getSliderLayout (juce::Slider& slider) override
                {
                    juce::Slider::SliderLayout layout;
                    if (slider.getName() == "LowBand Slope" || slider.getName() == "HighBand Slope") {
                        layout.textBoxBounds = {};
                        return layout;
                    }

                    auto r = slider.getLocalBounds();

                    const int w = slider.getWidth();
                    const int h = slider.getHeight();

                    layout.textBoxBounds = juce::Rectangle<int> (
                        r.getCentreX() - w / 2,
                        r.getCentreY() - h / 2,
                        w, h);

                    layout.sliderBounds = r;

                    return layout;
                }

                void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                           float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                           juce::Slider& slider) override
                {
                    // Basis-Implementation - wird in Subklassen überschrieben
                    auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
                    auto centreX = x + width * 0.5f;
                    auto centreY = y + height * 0.5f;
                    auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);

                    // Outer ring
                    g.setColour(Colours::BACKGROUNDCOLOUR);
                    g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);

                    // Indicator
                    juce::Path p;
                    auto pointerLength = radius * 0.6f;
                    auto pointerThickness = 2.0f;
                    p.addRectangle(-pointerThickness * 0.5f, -radius, pointerThickness, pointerLength);
                    p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));

                    g.setColour(Colours::ACCENTCOLOUR);
                    g.fillPath(p);
                }
            };
            class GainSliderLookAndFeel : public BaseSliderLookAndFeel
            {
            public:
                void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                                    float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                                    juce::Slider& slider) override
                {
                    bool isHovered = slider.isMouseOverOrDragging();
                    auto isEnabled = slider.isEnabled();
                    float hoverFactor = 1.0f;
                    auto surfaceCol = Colours::SURFACECOLOUR;
                    auto accentCol = Colours::ACCENTCOLOUR;
                    if (isEnabled) {
                        surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR;
                        accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR;
                        hoverFactor = isHovered ? 1.05f : 1.0f;
                    } else {
                        surfaceCol = Colours::SURFACEBYPASS;
                        accentCol = Colours::ACCENTWEAKCOLOUR;
                    }

                    auto radius = (juce::jmin(width / 2, height / 2) - 4.0f); // * hoverFactor;
                    auto centreX = x + width * 0.5f;
                    auto centreY = y + height * 0.5f;
                    auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);

                    auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle;
                    const float arcPathWidth = 3.0f;

                    // Background
                    g.setColour(Colours::BACKGROUNDCOLOUR);
                    g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f);

                    g.setColour(surfaceCol);
                    g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth);


                    // Arc für Gain-Anzeige
                    juce::Path valueArc;
                    valueArc.addCentredArc(centreX, centreY, radius, radius,
                                          0.0f, mid, angle, true);

                    g.setColour(accentCol);
                    g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));

                    // Pointer
                    juce::Path p;
                    p.addRectangle(-arcPathWidth / 2, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, radius * 0.5f * hoverFactor);
                    p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));
                    g.fillPath(p);

                }
            };

            class FreqQSliderLookAndFeel : public BaseSliderLookAndFeel
            {
            public:
                void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                                    float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                                    juce::Slider& slider) override
                {
                    auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
                    auto centreX = x + width * 0.5f;
                    auto centreY = y + height * 0.5f;
                    auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
                    const float arcPathWidth = 2.0f;
                    bool isHovered = slider.isMouseOverOrDragging();
                    bool isEnabled = slider.isEnabled();
                    float hoverFactor = 1.0f;
                    auto surfaceCol = Colours::SURFACECOLOUR;
                    auto accentCol = Colours::ACCENTCOLOUR;
                    if (isEnabled) {
                        surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR;
                        accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR;
                        hoverFactor = isHovered ? 1.05f : 1.0f;
                    } else {
                        surfaceCol = Colours::SURFACEBYPASS;
                        accentCol = Colours::ACCENTWEAKCOLOUR;
                    }


                    // Einfacher Ring
                    g.setColour(surfaceCol);
                    g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f);

                    juce::Path valueArc;
                    valueArc.addCentredArc(centreX, centreY, radius, radius,
                                          0.0f, rotaryStartAngle, angle, true);

                    g.setColour(accentCol);
                    g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
                    // Dünner Pointer
                    juce::Path p;
                    p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, radius * 0.4f * hoverFactor);
                    p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));

                    g.setColour(accentCol);
                    g.fillPath(p);
                }
            };

            class SlopeSliderLookAndFeel : public BaseSliderLookAndFeel {
            public:
                static constexpr float labelPadding = 20.0f;

                juce::Slider::SliderLayout getSliderLayout(juce::Slider& slider) override
                {
                    juce::Slider::SliderLayout layout;
                    auto bounds = slider.getLocalBounds();

                    // TextBox in der Mitte (wie bei BaseSliderLookAndFeel)
                    layout.textBoxBounds = juce::Rectangle<int>(
                        bounds.getCentreX() - bounds.getWidth() / 2,
                        bounds.getCentreY() - bounds.getHeight() / 2,
                        bounds.getWidth(),
                        bounds.getHeight()
                    );

                    // HIER IST DER TRICK: Knob-Bereich kleiner als Slider-Bounds!
                    // So bleibt Platz für die Labels außerhalb
                    layout.sliderBounds = bounds.reduced(labelPadding);

                    return layout;
                }

                void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                       float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                       juce::Slider& slider) override
                {
                    // Basis-Implementation - wird in Subklassen überschrieben
                    auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
                    auto centreX = x + width * 0.5f;
                    auto centreY = y + height * 0.5f;
                    auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
                    bool isEnabled = slider.isEnabled();
                    bool isHovered = slider.isMouseOverOrDragging();

                    // Outer ring
                    g.setColour(Colours::BACKGROUNDCOLOUR);
                    g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);

                    juce::Colour accentCol = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;

                    // Indicator
                    juce::Path p;
                    auto pointerLength = radius * 0.6f;
                    auto pointerThickness = 2.0f;
                    p.addRectangle(-pointerThickness * 0.5f, -radius, pointerThickness, pointerLength);
                    p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));

                    g.setColour(accentCol);
                    g.fillPath(p);


                    // Dann zeichne die Labels
                    const auto bounds = juce::Rectangle<int>(x, y, width, height);
                    const auto centre = bounds.getCentre().toFloat();
                    const float textRadius = std::min(width, height) * 0.5f + 5.0f;

                    const juce::StringArray labels = {"12", "24", "36", "48"};

                    g.setFont(12.0f);
                    auto value = slider.getValue();

                    for (int i = 0; i < labels.size(); ++i)
                    {
                        const double labelVal = labels[i].getDoubleValue();  // "12" -> 12.0 usw.
                        const bool isMatch = value == i; // Toleranz: 0.5

                        juce::Colour colour;
                        if (!isEnabled) {
                            colour = isMatch ? Colours::ACCENTWEAKCOLOUR : Colours::FOREGROUNDCOLOUR.withMultipliedAlpha (0.4f);
                        }
                        else {
                            colour = isMatch
                            ? Colours::ACCENTCOLOUR
                            : Colours::FOREGROUNDCOLOUR;
                        }
                        g.setColour(colour);


                        const double position = (double)i / 3.0;
                        const float ang = rotaryStartAngle + position * (rotaryEndAngle - rotaryStartAngle);

                        juce::Point<float> pos(
                            centre.x + textRadius * std::cos(ang - juce::MathConstants<float>::halfPi),
                            centre.y + textRadius * std::sin(ang - juce::MathConstants<float>::halfPi)
                        );

                        juce::Rectangle<float> textRect(30.0f, 16.0f);
                        textRect.setCentre(pos);

                        g.drawText(labels[i], textRect, juce::Justification::centred);
                    }

                }
            };

            class GlobalSliderLookAndFeel : public BaseSliderLookAndFeel
            {
            public:
                void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
                                    float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                                    juce::Slider& slider) override
                {
                    auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
                    auto centreX = x + width * 0.5f;
                    auto centreY = y + height * 0.5f;
                    auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
                    const float arcPathWidth = 2.0f;
                    auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle;
                    bool isHovered = slider.isMouseOverOrDragging();
                    bool isEnabled = slider.isEnabled();
                    float hoverFactor = 1.0f;
                    auto surfaceCol = Colours::SURFACECOLOUR;
                    auto accentCol = Colours::ACCENTCOLOUR;
                    auto ringCol = Colours::SURFACEHOVER;
                    if (isEnabled) {
                        surfaceCol = isHovered ? Colours::SURFACEHOVER : Colours::SURFACECOLOUR;
                        accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR;
                        ringCol = isHovered ? Colours::SURFACECOLOUR : Colours::SURFACEHOVER;
                        hoverFactor = isHovered ? 1.05f : 1.0f;
                    } else {
                        surfaceCol = Colours::SURFACEBYPASS;
                        accentCol = Colours::ACCENTWEAKCOLOUR;
                        ringCol = Colours::SURFACEHOVER;
                    }


                    // Einfacher Ring
                    g.setColour(surfaceCol);
                    g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f);

                    g.setColour(ringCol);
                    g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth);

                    juce::Path valueArc;
                    valueArc.addCentredArc(centreX, centreY, radius, radius,
                                          0.0f, mid, angle, true);

                    g.setColour(accentCol);
                    g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
                    // Dünner Pointer
                    juce::Path p;
                    p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, radius * 0.4f * hoverFactor);
                    p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));

                    g.setColour(accentCol);
                    g.fillPath(p);
                }
            };

            struct SliderStyles {
                struct Size {
                    enum class SizeMode {
                        Gain,
                        Freq,
                        Q,
                        Slope,
                        Global,
                        Mix
                    };
                    static float getSliderSizeMod(SizeMode mode) {
                        switch (mode) {
                            case SizeMode::Gain: return 1.25f;
                            case SizeMode::Freq: return 0.8f;
                            case SizeMode::Q: return 0.8f;
                            case SizeMode::Slope: return 0.7f;
                            case SizeMode::Global: return 0.8f;
                            case SizeMode::Mix: return 0.8f;
                            default: return 1.0f;
                        }
                    }

                };
            };

            struct LabelStyles {
            };

            struct TextInputStyles {
            };

            struct ComboBoxStyles {
            };
        };

        struct Assets {
        };

        struct Layout {

            ///
            /// @param mode Spacing mode -- e.g. Spacing::SizeMode::XS
            /// @return Gap size in px as int
            static int gap(Spacing::SizeMode mode) {
                return juce::roundToInt(Spacing::scale.space(mode));
            }

            static juce::BorderSize<int> padding(Spacing::SizeMode mode) {
                if (mode == Spacing::SizeMode::XS) return {0, 0, 0, 0};
                const int px = gap(mode);
                return juce::BorderSize<int>{px, px, px, px};
            }

            static juce::Rectangle<int> content(juce::Rectangle<int> rect, juce::BorderSize<int> pad) {
                rect = pad.subtractedFrom(rect);
                return rect;
            }

            static juce::FlexItem flexItem(juce::Component& component,
                                           float flexGrow = 1.f,
                                           float flexShrink = 1.f,
                                           float basisPx = -1.f,
                                           Spacing::SizeMode margin = Spacing::SizeMode::XS) {

                juce::FlexItem item{ component };
                item.flexGrow   = flexGrow;
                item.flexShrink = flexShrink;

                if (basisPx >= 0.f)
                    item.flexBasis = basisPx;

                const float half = (float) gap(margin) * 0.5f;
                item.margin = juce::FlexItem::Margin{ half, half, half, half };

                return item;
            }

            static void flexRow(juce::Rectangle<int> bounds,
                                juce::Array<juce::FlexItem> items,
                                Spacing::SizeMode gapSize,
                                juce::FlexBox::JustifyContent justifyContent,
                                juce::FlexBox::AlignItems alignItems
                                ) {
                const auto pad = padding(gapSize);
                juce::FlexBox fb;
                fb.flexDirection  = juce::FlexBox::Direction::row;
                fb.flexWrap       = juce::FlexBox::Wrap::noWrap;
                fb.justifyContent = justifyContent;
                fb.alignItems   = alignItems;

                fb.items.addArray(items);

                fb.performLayout(content(bounds, pad));

            }

            static void flexColumn(juce::Rectangle<int> bounds,
                                   juce::Array<juce::FlexItem> items,
                                   Spacing::SizeMode gapSize,
                                   juce::FlexBox::JustifyContent justifyContent,
                                   juce::FlexBox::AlignItems alignItems
                                   ) {
                const auto pad = padding(gapSize);
                juce::FlexBox fb;
                fb.flexDirection  = juce::FlexBox::Direction::column;
                fb.flexWrap       = juce::FlexBox::Wrap::noWrap;
                fb.justifyContent = justifyContent;
                fb.alignItems   = alignItems;

                fb.items.addArray(items);
                fb.performLayout(content(bounds, pad));
            }

            static juce::Grid::TrackInfo fr(float value) {
                jassert(value > 0.0f);
                return juce::Grid::TrackInfo{juce::Grid::Fr{juce::roundToInt(value)}};
            }

            static juce::Grid::TrackInfo pxTrack(float value) {
                jassert(value >= 0.0f);
                return juce::Grid::TrackInfo{juce::Grid::Px{value}};
            }

            static juce::Grid::Px px(float value) {
                jassert(value >= 0.0f);
                return juce::Grid::Px{value};
            }

            struct GridSpec {
                std::vector<juce::Grid::TrackInfo> cols;
                std::vector<juce::Grid::TrackInfo> rows;
                Spacing::SizeMode colGap { Spacing::SizeMode::M };
                Spacing::SizeMode rowGap { Spacing::SizeMode::M };
                juce::BorderSize<int> pad { 0, 0, 0, 0 };
            };

            static juce::GridItem area(juce::Component& c,
                           int rowStart, int colStart,
                           int rowEnd,   int colEnd)
            {
                return juce::GridItem(c)
                .withArea(rowStart, colStart, rowEnd, colEnd);

            }

            static void grid(juce::Rectangle<int> bounds,
                             const GridSpec& spec,
                             std::initializer_list<juce::GridItem> children,
                             juce::Grid::AutoFlow autoFlow = juce::Grid::AutoFlow::row) {
                juce::Grid g;

                for (const auto& t : spec.cols) g.templateColumns.add(t);
                for (const auto& t : spec.rows) g.templateRows.add(t);

                g.autoFlow    = autoFlow;
                g.autoColumns = juce::Grid::TrackInfo(juce::Grid::Fr(1));
                g.autoRows    = juce::Grid::TrackInfo(juce::Grid::Fr(1));

                g.columnGap =  px((float) gap(spec.colGap));
                g.rowGap    = px((float) gap(spec.rowGap));

                for (const auto& c : children)
                    g.items.add(c);

                const auto inner = spec.pad.subtractedFrom(bounds);
                g.performLayout(inner);
            }
        };

        class PresetMenu : public juce::Component {
        public:
            PresetMenu(juce::TextEditor* inputField,
               juce::TextButton* saveButton,
               juce::TextButton* deleteButton) {
                addAndMakeVisible(*inputField);
                addAndMakeVisible(*saveButton);
                addAndMakeVisible(*deleteButton);
            }

        };
        class PresetMenuLookAndFeel : public juce::LookAndFeel_V4 {
        public:
            void drawCallOutBoxBackground(juce::CallOutBox &callOutBox, juce::Graphics &g, const juce::Path &path, juce::Image &image) override {
                g.setColour(Colours::SURFACECOLOUR);
                g.fillPath(path);

                g.setColour (Colours::FOREGROUNDCOLOUR.withAlpha(0.3f));
                g.strokePath (path, juce::PathStrokeType (1.0f));
            };


            int getCallOutBoxBorderSize (const juce::CallOutBox&) override
            {
                return 4;
            }
        };

        class PresetButtonsLookAndFeel : public juce::LookAndFeel_V4 {
        public:

            void drawButtonText(juce::Graphics &g, juce::TextButton &button, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override {
                auto bounds = button.getLocalBounds();

                // Text holen
                auto text  = button.getButtonText();

                // Farbe wählen
                auto isHovered = button.isMouseOver();
                bool isEnabled = button.isEnabled();
                juce::Colour colour = isHovered ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR;

                if (!isEnabled) colour = colour.withMultipliedAlpha(0.6f);


                g.setColour(colour);

                // Font einstellen
                auto font = Typography::getFont(Typography::Style::Button, 1.0f);
                g.setFont(font);

                // Text zeichnen
                g.drawFittedText(text,
                                 bounds.reduced(4),
                                 juce::Justification::centred,
                                 1);
            }
        void drawButtonBackground(juce::Graphics& g, juce::Button& button,
                      const juce::Colour& backgroundColour,
                      bool shouldDrawButtonAsHighlighted,
                      bool shouldDrawButtonAsDown) override
        {
            auto bounds = button.getLocalBounds().toFloat();

            bool isHovered = button.isMouseOver();
                bool isEnabled = button.isEnabled();

            auto colour = isHovered ? Colours::BACKGROUNDHOVER : Colours::BACKGROUNDCOLOUR;
                if (!isEnabled) colour = colour.withMultipliedAlpha(0.6f);

            g.setColour(colour);
            g.fillRoundedRectangle(bounds.reduced(1.0f), 4.0f);
        }
    };

    class PresetComboBoxLookAndFeel : public juce::LookAndFeel_V4 {
        public:
        juce::PopupMenu::Options getOptionsForComboBoxPopupMenu(juce::ComboBox& box,
                                                      juce::Label& label) override
        {
            // Basis-Options holen
            auto options = juce::LookAndFeel_V4::getOptionsForComboBoxPopupMenu(box, label);

            // Anzahl der sichtbaren Einträge
            const int visibleRows = 5;

            // Höhe einer Zeile
            auto font = getComboBoxFont(box);
            int rowHeight = (int)std::ceil(font.getHeight() * 1.6f);

            // Maximale Höhe des Popups
            int maxPopupHeight = visibleRows * rowHeight;

            // Screen-Bounds der ComboBox
            auto boxScreen = box.getScreenBounds();

            // Wichtig: Ein sehr breiter aber in der Höhe begrenzter Bereich
            // JUCE wird das Popup darin zentrieren
            juce::Rectangle<int> constrainedArea(
                0,                                    // X: ganze Screen-Breite
                boxScreen.getBottom(),                // Y: direkt unter der Box
                juce::Desktop::getInstance().getDisplays()
                    .getPrimaryDisplay()->totalArea.getWidth(),
                maxPopupHeight                        // Höhe: nur unsere gewünschten Zeilen
            );

            options = options.withTargetScreenArea(constrainedArea)
                             .withStandardItemHeight(rowHeight);

            return options;
        }
    };





    private:
    };
}


#endif //AXIOMDESIGNSYSTEM_H
