#pragma once

#include <cstdint>
#include <cstddef>
#include <cstring>

// Maximum number of widgets
static constexpr size_t MAX_WIDGETS = 64;
static constexpr size_t MAX_SCREENS = 8;
static constexpr size_t MAX_TEXT_LEN = 32;
static constexpr size_t MAX_SCREEN_NAME_LEN = 24;
static constexpr size_t MAX_BG_IMAGE_PATH_LEN = 48;
static constexpr size_t CHART_MAX_SERIES = 3;
static constexpr size_t MAX_CONDITIONS = 3;
static constexpr size_t MAX_FORMAT_LEN = 16;  // Shorter format strings for left/right values
static constexpr size_t MAX_SUBBUTTONS = 6;   // Sub-buttons for RoomCard
static constexpr size_t MAX_TEXTLINES = 5;    // Variable text lines for RoomCard

enum class WidgetType : uint8_t {
    LABEL = 0,
    BUTTON = 1,
    LED = 2,
    ICON = 3,
    TABVIEW = 4,
    TABPAGE = 5,
    POWERFLOW = 6,
    POWERNODE = 7,
    POWERLINK = 8,
    CHART = 9,
    CLOCK = 10,
    ROOMCARD = 11,
    RECTANGLE = 12,
    ARC = 13,
    BUTTONMATRIX = 14,
};

enum class IconPosition : uint8_t {
    LEFT = 0,
    RIGHT = 1,
    TOP = 2,
    BOTTOM = 3,
};

enum class ScreenMode : uint8_t {
    FULLSCREEN = 0,
    MODAL = 1,
};

enum class BgImageMode : uint8_t {
    NONE = 0,
    STRETCH = 1,
    CENTER = 2,
    TILE = 3,
};

enum class ButtonAction : uint8_t {
    KNX = 0,
    JUMP = 1,
    BACK = 2,
};

enum class ChartPeriod : uint8_t {
    HOUR_1 = 0,
    HOUR_3 = 1,
    HOUR_5 = 2,
    HOUR_12 = 3,
    HOUR_24 = 4,
    MONTH_1 = 5,
};

// Text source: static text or KNX group address
enum class TextSource : uint8_t {
    STATIC = 0,      // Static text
    KNX_DPT_TEMP = 1,    // KNX Temperature (DPT 9.001)
    KNX_DPT_SWITCH = 2,  // KNX Switch (DPT 1.001)
    KNX_DPT_PERCENT = 3, // KNX Percent (DPT 5.001)
    KNX_DPT_TEXT = 4,    // KNX Text (DPT 16.000)
    KNX_DPT_POWER = 5,   // KNX Power (DPT 14.056)
    KNX_DPT_ENERGY = 6,  // KNX Energy (DPT 13.013)
    KNX_DPT_DECIMALFACTOR = 7, // KNX Decimal Factor (DPT 5.005)
    KNX_DPT_TIME = 8,    // KNX Time of day (DPT 10.001)
    KNX_DPT_DATE = 9,    // KNX Date (DPT 11.001)
    KNX_DPT_DATETIME = 10, // KNX DateTime (DPT 19.001)
    SYSTEM_TIME = 11,      // System Time (RTC)
    SYSTEM_DATE = 12,      // System Date (RTC)
    SYSTEM_DATETIME = 13,  // System DateTime (RTC)
};

enum class TextAlign : uint8_t {
    LEFT = 0,
    CENTER = 1,
    RIGHT = 2,
};

// Color as RGB888
struct Color {
    uint8_t r, g, b;

    uint32_t toLvColor() const {
        return (r << 16) | (g << 8) | b;
    }

    static Color fromHex(uint32_t hex) {
        return {
            .r = static_cast<uint8_t>((hex >> 16) & 0xFF),
            .g = static_cast<uint8_t>((hex >> 8) & 0xFF),
            .b = static_cast<uint8_t>(hex & 0xFF)
        };
    }

    // Invert lightness via HSL: light colors become dark, dark become light
    Color invertLightness() const {
        float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f;
        float maxC = rf > gf ? (rf > bf ? rf : bf) : (gf > bf ? gf : bf);
        float minC = rf < gf ? (rf < bf ? rf : bf) : (gf < bf ? gf : bf);
        float delta = maxC - minC;
        float L = (maxC + minC) * 0.5f;
        float S = 0.0f, H = 0.0f;
        if (delta > 0.0001f) {
            S = L < 0.5f ? delta / (maxC + minC) : delta / (2.0f - maxC - minC);
            if (maxC == rf)      H = (gf - bf) / delta + (gf < bf ? 6.0f : 0.0f);
            else if (maxC == gf) H = (bf - rf) / delta + 2.0f;
            else                 H = (rf - gf) / delta + 4.0f;
            H /= 6.0f;
        }
        // Invert lightness
        L = 1.0f - L;
        // HSL to RGB
        auto hue2rgb = [](float p, float q, float t) -> float {
            if (t < 0.0f) t += 1.0f;
            if (t > 1.0f) t -= 1.0f;
            if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
            if (t < 0.5f) return q;
            if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
            return p;
        };
        float ro, go, bo;
        if (S < 0.0001f) {
            ro = go = bo = L;
        } else {
            float q = L < 0.5f ? L * (1.0f + S) : L + S - L * S;
            float p = 2.0f * L - q;
            ro = hue2rgb(p, q, H + 1.0f / 3.0f);
            go = hue2rgb(p, q, H);
            bo = hue2rgb(p, q, H - 1.0f / 3.0f);
        }
        return {
            .r = static_cast<uint8_t>(ro * 255.0f + 0.5f),
            .g = static_cast<uint8_t>(go * 255.0f + 0.5f),
            .b = static_cast<uint8_t>(bo * 255.0f + 0.5f)
        };
    }
};

// Condition operator for conditional styling
enum class ConditionOp : uint8_t {
    LESS = 0,           // value < threshold
    LESS_EQUAL = 1,     // value <= threshold
    EQUAL = 2,          // value == threshold
    GREATER_EQUAL = 3,  // value >= threshold
    GREATER = 4,        // value > threshold
    NOT_EQUAL = 5,      // value != threshold
};

// Which KNX address to use for condition evaluation
enum class ConditionSource : uint8_t {
    PRIMARY = 0,    // knxAddress (bottom value)
    SECONDARY = 1,  // knxAddress2 (left value)
    TERTIARY = 2,   // knxAddress3 (right value)
};

// Style to apply when condition is met
struct ConditionStyle {
    uint32_t iconCodepoint;    // 0 = don't change icon
    Color textColor;
    Color bgColor;
    uint8_t bgOpacity;
    uint8_t flags;             // Bits: 0=useTextColor, 1=useBgColor, 2=useBgOpacity, 3=hide

    static constexpr uint8_t FLAG_USE_TEXT_COLOR = 0x01;
    static constexpr uint8_t FLAG_USE_BG_COLOR = 0x02;
    static constexpr uint8_t FLAG_USE_BG_OPACITY = 0x04;
    static constexpr uint8_t FLAG_HIDE = 0x08;
};

// A single style condition
struct StyleCondition {
    float threshold;           // 4 bytes
    ConditionOp op;            // 1 byte
    ConditionSource source;    // 1 byte - which value to check
    uint8_t priority;          // 1 byte (lower = higher priority)
    ConditionStyle style;      // 12 bytes
    bool enabled;              // 1 byte
    // Total: 20 bytes per condition
};

// Sub-button position around RoomCard bubble (8 positions, 45° apart)
enum class SubButtonPosition : uint8_t {
    TOP = 0,
    TOP_RIGHT = 1,
    RIGHT = 2,
    BOTTOM_RIGHT = 3,
    BOTTOM = 4,
    BOTTOM_LEFT = 5,
    LEFT = 6,
    TOP_LEFT = 7,
};

// Sub-button action type
enum class SubButtonAction : uint8_t {
    TOGGLE_KNX = 0,    // Toggle KNX switch
    NAVIGATE = 1,      // Navigate to screen
};

// Sub-button configuration for RoomCard (30 bytes)
struct SubButtonConfig {
    uint32_t iconCodepointOff;   // 4 bytes - Icon codepoint when OFF
    uint32_t iconCodepointOn;    // 4 bytes - Icon codepoint when ON (0 = use iconCodepointOff)
    uint16_t knxAddrRead;        // 2 bytes - KNX address to read status
    uint16_t knxAddrWrite;       // 2 bytes - KNX address to write on click
    Color bgColorOn;             // 3 bytes - Background color when ON
    Color bgColorOff;            // 3 bytes - Background color when OFF
    Color iconColorOn;           // 3 bytes - Icon color when ON
    Color iconColorOff;          // 3 bytes - Icon color when OFF
    SubButtonPosition position;  // 1 byte - Position around bubble
    SubButtonAction action;      // 1 byte - Action type
    uint8_t targetScreen;        // 1 byte - Target screen for navigate
    bool enabled;                // 1 byte - Is this sub-button active?
    uint8_t _padding[2];         // 2 bytes - Alignment padding
    // Total: 30 bytes per SubButton
};

// Text line configuration for RoomCard (24 bytes)
struct TextLineConfig {
    uint32_t iconCodepoint;      // 4 bytes - Icon codepoint (0 = no icon)
    uint16_t knxAddr;            // 2 bytes - KNX address for dynamic value
    TextSource textSrc;          // 1 byte - Text source (static or KNX DPT)
    uint8_t fontSize;            // 1 byte - Font size index (0-5)
    bool enabled;                // 1 byte - Is this text line active?
    uint8_t _padding;            // 1 byte - Alignment padding
    char text[MAX_FORMAT_LEN];   // 16 bytes - Static text or format string
    // Total: 26 bytes per TextLine
};

// Shadow configuration
struct ShadowConfig {
    int8_t offsetX;
    int8_t offsetY;
    uint8_t blur;
    uint8_t spread;
    Color color;
    bool enabled;
};

// Widget configuration
struct WidgetConfig {
    // Basic properties
    uint8_t id;              // Unique ID (0-255)
    WidgetType type;
    int16_t x, y;
    int16_t width, height;
    bool visible;

    // Text properties
    TextSource textSource;
    char text[MAX_TEXT_LEN]; // Static text or format string
    uint16_t knxAddress;     // KNX group address (GA) for read binding
    uint8_t fontSize;        // Font size index (0=14, 1=18, 2=22, 3=28, 4=36, 5=48)
    uint8_t textAlign;       // TextAlign: 0=left, 1=center, 2=right
    bool isContainer;        // For buttons: use as container (no internal label/icon)

    // Colors
    Color textColor;
    Color bgColor;
    uint8_t bgOpacity;       // 0-255
    uint8_t borderRadius;
    uint8_t borderWidth;     // 0-32
    Color borderColor;
    uint8_t borderOpacity;   // 0-255

    // Shadow
    ShadowConfig shadow;

    // Button specific
    bool isToggle;           // For buttons: toggle mode
    uint16_t knxAddressWrite; // KNX group address (GA) to write on click
    ButtonAction action;     // Button action (KNX, Jump, Back)
    uint8_t targetScreen;    // Target screen ID for jump

    // Icon properties (for Label, Button, Icon widgets)
    uint32_t iconCodepoint;  // Unicode codepoint (0 = no icon)
    uint8_t iconPosition;    // IconPosition: 0=left, 1=right, 2=top, 3=bottom
    uint8_t iconSize;        // Font size index (0-13 for icons)
    int8_t iconGap;          // Gap between icon and text (px)
    int16_t iconPositionX;   // Icon Pos X (for decorative icons in Tile)
    int16_t iconPositionY;   // Icon Pos Y (for decorative icons in Tile)

    // Hierarchy
    int8_t parentId;         // ID of parent widget (-1 = root/screen)

    // Chart properties
    uint8_t chartPeriod;
    uint8_t chartSeriesCount;
    uint16_t chartKnxAddress[CHART_MAX_SERIES];
    TextSource chartTextSource[CHART_MAX_SERIES];
    Color chartSeriesColor[CHART_MAX_SERIES];

    // Secondary KNX address (for PowerNode LEFT value)
    uint16_t knxAddress2;
    TextSource textSource2;
    char text2[MAX_FORMAT_LEN];  // Format string for left value (short)

    // Tertiary KNX address (for PowerNode RIGHT value)
    uint16_t knxAddress3;
    TextSource textSource3;
    char text3[MAX_FORMAT_LEN];  // Format string for right value (short)

    // Conditional styling
    uint8_t conditionCount;
    StyleCondition conditions[MAX_CONDITIONS];

    // RoomCard sub-buttons
    uint8_t subButtonCount;
    uint8_t subButtonSize;      // Sub-button size in pixels (default 40)
    uint8_t subButtonDistance;  // Distance from center in pixels (default 80)
    uint8_t subButtonOpacity;   // Sub-button opacity 0-255 (default 255)
    uint8_t cardStyle;          // 0=Bubble (round), 1=Tile (rectangular)
    SubButtonConfig subButtons[MAX_SUBBUTTONS];

    // RoomCard text lines (for Tile style)
    uint8_t textLineCount;
    TextLineConfig textLines[MAX_TEXTLINES];

    // Arc properties
    int16_t arcMin;
    int16_t arcMax;
    uint8_t arcUnit;          // 0=none, 1=percent, 2=C
    bool arcShowValue;        // Show center value label
    int8_t arcScaleOffset;    // Radial offset for scale (ticks/labels)
    Color arcScaleColor;      // Scale (tick/label) color
    Color arcValueColor;      // Center value text color
    uint8_t arcValueFontSize; // Center value font size index

    // Theme
    bool themeFixed;          // true = colors stay fixed in night mode

    // Serialization size (fixed for NVS storage)
    static constexpr size_t SERIALIZED_SIZE = 406;

    void serialize(uint8_t* buf) const;
    void deserialize(const uint8_t* buf);

    // Create default label
    static WidgetConfig createLabel(uint8_t id, int16_t x, int16_t y, const char* text);

    // Create KNX-bound label
    static WidgetConfig createKnxLabel(uint8_t id, int16_t x, int16_t y,
                                        TextSource source, uint16_t knxAddr, const char* format);

    // Create button
    static WidgetConfig createButton(uint8_t id, int16_t x, int16_t y,
                                     const char* text, uint16_t knxAddrWrite, bool toggle);
};

// Screen configuration (holds all widgets)
struct ScreenConfig {
    uint8_t id;
    char name[MAX_SCREEN_NAME_LEN];
    ScreenMode mode;
    Color backgroundColor;
    Color nightBackgroundColor;  // Background color for night mode
    char bgImagePath[MAX_BG_IMAGE_PATH_LEN];  // Background image path (e.g., "/images/bg.png")
    BgImageMode bgImageMode;                   // 0=none, 1=stretch, 2=center, 3=tile
    uint8_t widgetCount;
    WidgetConfig widgets[MAX_WIDGETS];

    // Modal-specific properties (only used when mode == MODAL)
    int16_t modalX;          // Modal position X (0 = centered)
    int16_t modalY;          // Modal position Y (0 = centered)
    int16_t modalWidth;      // Modal width (0 = auto from content)
    int16_t modalHeight;     // Modal height (0 = auto from content)
    uint8_t modalBorderRadius;
    bool modalDimBackground; // Dim the background behind modal

    void clear(uint8_t newId = 0, const char* newName = nullptr);
    int addWidget(const WidgetConfig& widget);  // Returns widget ID or -1
    bool removeWidget(uint8_t id);
    WidgetConfig* findWidget(uint8_t id);
    const WidgetConfig* findWidget(uint8_t id) const;
};

// Screen transition animation types
enum class ScreenAnimType : uint8_t {
    NONE = 0,
    FADE = 1,
    SLIDE_LEFT = 2,
    SLIDE_RIGHT = 3,
    SLIDE_UP = 4,
    SLIDE_DOWN = 5,
    OVER_LEFT = 6,
    OVER_RIGHT = 7,
    OVER_UP = 8,
    OVER_DOWN = 9,
};

struct GuiConfig {
    uint8_t screenCount;
    ScreenConfig screens[MAX_SCREENS];
    uint8_t startScreenId;
    bool standbyEnabled;
    uint8_t standbyScreenId;
    uint16_t standbyMinutes;
    uint16_t knxTimeAddress;
    uint16_t knxDateAddress;
    uint16_t knxDateTimeAddress;
    uint16_t knxNightModeAddress;

    // Screen transition animation
    ScreenAnimType screenAnimType;
    uint16_t screenAnimDuration;  // Duration in ms (0 = default 300ms)

    void clear();
    ScreenConfig* findScreen(uint8_t id);
    const ScreenConfig* findScreen(uint8_t id) const;
};
