#include "WidgetConfig.hpp"
#include <cstring>
#include <cstdio>

// WidgetConfig implementation
void WidgetConfig::serialize(uint8_t* buf) const {
    memset(buf, 0, SERIALIZED_SIZE);
    size_t pos = 0;

    buf[pos++] = id;
    buf[pos++] = static_cast<uint8_t>(type);
    buf[pos++] = x & 0xFF; buf[pos++] = (x >> 8) & 0xFF;
    buf[pos++] = y & 0xFF; buf[pos++] = (y >> 8) & 0xFF;
    buf[pos++] = width & 0xFF; buf[pos++] = (width >> 8) & 0xFF;
    buf[pos++] = height & 0xFF; buf[pos++] = (height >> 8) & 0xFF;
    buf[pos++] = visible ? 1 : 0;

    buf[pos++] = static_cast<uint8_t>(textSource);
    memcpy(&buf[pos], text, MAX_TEXT_LEN); pos += MAX_TEXT_LEN;
    buf[pos++] = knxAddress & 0xFF; buf[pos++] = (knxAddress >> 8) & 0xFF;
    buf[pos++] = fontSize;
    buf[pos++] = textAlign;
    buf[pos++] = isContainer ? 1 : 0;

    buf[pos++] = textColor.r; buf[pos++] = textColor.g; buf[pos++] = textColor.b;
    buf[pos++] = bgColor.r; buf[pos++] = bgColor.g; buf[pos++] = bgColor.b;
    buf[pos++] = bgOpacity;
    buf[pos++] = borderRadius;

    buf[pos++] = shadow.offsetX;
    buf[pos++] = shadow.offsetY;
    buf[pos++] = shadow.blur;
    buf[pos++] = shadow.spread;
    buf[pos++] = shadow.color.r; buf[pos++] = shadow.color.g; buf[pos++] = shadow.color.b;
    buf[pos++] = shadow.enabled ? 1 : 0;

    buf[pos++] = isToggle ? 1 : 0;
    buf[pos++] = knxAddressWrite & 0xFF; buf[pos++] = (knxAddressWrite >> 8) & 0xFF;
    buf[pos++] = static_cast<uint8_t>(action);
    buf[pos++] = targetScreen;

    // Icon properties
    buf[pos++] = iconCodepoint & 0xFF;
    buf[pos++] = (iconCodepoint >> 8) & 0xFF;
    buf[pos++] = (iconCodepoint >> 16) & 0xFF;
    buf[pos++] = (iconCodepoint >> 24) & 0xFF;
    buf[pos++] = iconPosition;
    buf[pos++] = iconSize;
    buf[pos++] = static_cast<uint8_t>(iconGap);
    
    // Hierarchy
    buf[pos++] = static_cast<uint8_t>(parentId);
}

void WidgetConfig::deserialize(const uint8_t* buf) {
    size_t pos = 0;

    id = buf[pos++];
    type = static_cast<WidgetType>(buf[pos++]);
    x = buf[pos] | (buf[pos+1] << 8); pos += 2;
    y = buf[pos] | (buf[pos+1] << 8); pos += 2;
    width = buf[pos] | (buf[pos+1] << 8); pos += 2;
    height = buf[pos] | (buf[pos+1] << 8); pos += 2;
    visible = buf[pos++] != 0;

    textSource = static_cast<TextSource>(buf[pos++]);
    memcpy(text, &buf[pos], MAX_TEXT_LEN); pos += MAX_TEXT_LEN;
    text[MAX_TEXT_LEN - 1] = '\0';
    knxAddress = buf[pos] | (buf[pos+1] << 8); pos += 2;
    fontSize = buf[pos++];
    textAlign = buf[pos++];
    isContainer = buf[pos++] != 0;

    textColor.r = buf[pos++]; textColor.g = buf[pos++]; textColor.b = buf[pos++];
    bgColor.r = buf[pos++]; bgColor.g = buf[pos++]; bgColor.b = buf[pos++];
    bgOpacity = buf[pos++];
    borderRadius = buf[pos++];

    shadow.offsetX = static_cast<int8_t>(buf[pos++]);
    shadow.offsetY = static_cast<int8_t>(buf[pos++]);
    shadow.blur = buf[pos++];
    shadow.spread = buf[pos++];
    shadow.color.r = buf[pos++]; shadow.color.g = buf[pos++]; shadow.color.b = buf[pos++];
    shadow.enabled = buf[pos++] != 0;

    isToggle = buf[pos++] != 0;
    knxAddressWrite = buf[pos] | (buf[pos+1] << 8);
    pos += 2;
    action = static_cast<ButtonAction>(buf[pos++]);
    targetScreen = buf[pos++];

    // Icon properties
    iconCodepoint = buf[pos] | (buf[pos+1] << 8) | (buf[pos+2] << 16) | (buf[pos+3] << 24);
    pos += 4;
    iconPosition = buf[pos++];
    iconSize = buf[pos++];
    iconGap = static_cast<int8_t>(buf[pos++]);
    
    // Hierarchy
    parentId = static_cast<int8_t>(buf[pos++]);
}

WidgetConfig WidgetConfig::createLabel(uint8_t id, int16_t x, int16_t y, const char* labelText) {
    WidgetConfig cfg = {};
    cfg.id = id;
    cfg.parentId = -1; // Root
    cfg.type = WidgetType::LABEL;
    cfg.x = x;
    cfg.y = y;
    cfg.width = 150;
    cfg.height = 40;
    cfg.visible = true;
    cfg.textSource = TextSource::STATIC;
    strncpy(cfg.text, labelText, MAX_TEXT_LEN - 1);
    cfg.fontSize = 1; // 18pt
    cfg.textAlign = static_cast<uint8_t>(TextAlign::LEFT);
    cfg.isContainer = false;
    cfg.textColor = {255, 255, 255};
    cfg.bgColor = {0, 0, 0};
    cfg.bgOpacity = 0;
    cfg.borderRadius = 0;
    cfg.shadow.enabled = false;
    // Icon defaults
    cfg.iconCodepoint = 0;
    cfg.iconPosition = 0;
    cfg.iconSize = 1;
    cfg.iconGap = 8;
    return cfg;
}

WidgetConfig WidgetConfig::createKnxLabel(uint8_t id, int16_t x, int16_t y,
                                           TextSource source, uint16_t knxAddr, const char* format) {
    WidgetConfig cfg = createLabel(id, x, y, format);
    cfg.textSource = source;
    cfg.knxAddress = knxAddr;
    return cfg;
}

WidgetConfig WidgetConfig::createButton(uint8_t id, int16_t x, int16_t y,
                                        const char* labelText, uint16_t knxAddrWrite, bool toggle) {
    WidgetConfig cfg = {};
    cfg.id = id;
    cfg.parentId = -1; // Root
    cfg.type = WidgetType::BUTTON;
    cfg.x = x;
    cfg.y = y;
    cfg.width = 120;
    cfg.height = 50;
    cfg.visible = true;
    cfg.textSource = TextSource::STATIC;
    strncpy(cfg.text, labelText, MAX_TEXT_LEN - 1);
    cfg.fontSize = 1;
    cfg.textAlign = static_cast<uint8_t>(TextAlign::CENTER);
    cfg.isContainer = true;
    cfg.textColor = {255, 255, 255};
    cfg.bgColor = {33, 150, 243}; // Blue
    cfg.bgOpacity = 255;
    cfg.borderRadius = 8;
    cfg.shadow.enabled = true;
    cfg.shadow.offsetX = 2;
    cfg.shadow.offsetY = 2;
    cfg.shadow.blur = 8;
    cfg.shadow.spread = 0;
    cfg.shadow.color = {0, 0, 0};
    cfg.isToggle = toggle;
    cfg.knxAddressWrite = knxAddrWrite;
    cfg.action = ButtonAction::KNX;
    cfg.targetScreen = 0;
    // Icon defaults
    cfg.iconCodepoint = 0;
    cfg.iconPosition = 0;
    cfg.iconSize = 1;
    cfg.iconGap = 8;
    return cfg;
}

// ScreenConfig implementation
void ScreenConfig::clear(uint8_t newId, const char* newName) {
    id = newId;
    mode = ScreenMode::FULLSCREEN;
    backgroundColor = {26, 26, 46}; // Dark blue background
    widgetCount = 0;
    memset(widgets, 0, sizeof(widgets));
    memset(name, 0, sizeof(name));
    if (newName && newName[0] != '\0') {
        strncpy(name, newName, sizeof(name) - 1);
    }
    // Modal defaults
    modalX = 0;          // 0 = centered
    modalY = 0;          // 0 = centered
    modalWidth = 0;      // 0 = auto
    modalHeight = 0;     // 0 = auto
    modalBorderRadius = 12;
    modalDimBackground = true;
}

int ScreenConfig::addWidget(const WidgetConfig& widget) {
    if (widgetCount >= MAX_WIDGETS) return -1;

    // Find next free ID
    uint8_t newId = 0;
    for (uint8_t i = 0; i < widgetCount; i++) {
        if (widgets[i].id >= newId) newId = widgets[i].id + 1;
    }

    widgets[widgetCount] = widget;
    widgets[widgetCount].id = newId;
    widgetCount++;
    return newId;
}

bool ScreenConfig::removeWidget(uint8_t id) {
    for (uint8_t i = 0; i < widgetCount; i++) {
        if (widgets[i].id == id) {
            // Shift remaining widgets
            for (uint8_t j = i; j < widgetCount - 1; j++) {
                widgets[j] = widgets[j + 1];
            }
            widgetCount--;
            return true;
        }
    }
    return false;
}

WidgetConfig* ScreenConfig::findWidget(uint8_t id) {
    for (uint8_t i = 0; i < widgetCount; i++) {
        if (widgets[i].id == id) return &widgets[i];
    }
    return nullptr;
}

const WidgetConfig* ScreenConfig::findWidget(uint8_t id) const {
    for (uint8_t i = 0; i < widgetCount; i++) {
        if (widgets[i].id == id) return &widgets[i];
    }
    return nullptr;
}

// GuiConfig implementation
void GuiConfig::clear() {
    screenCount = 0;
    startScreenId = 0;
    standbyEnabled = false;
    standbyScreenId = 0xFF;
    standbyMinutes = 0;
    for (size_t i = 0; i < MAX_SCREENS; i++) {
        screens[i].clear(static_cast<uint8_t>(i), nullptr);
    }
}

ScreenConfig* GuiConfig::findScreen(uint8_t id) {
    for (uint8_t i = 0; i < screenCount; i++) {
        if (screens[i].id == id) return &screens[i];
    }
    return nullptr;
}

const ScreenConfig* GuiConfig::findScreen(uint8_t id) const {
    for (uint8_t i = 0; i < screenCount; i++) {
        if (screens[i].id == id) return &screens[i];
    }
    return nullptr;
}
