#include "PowerNodeWidget.hpp"
#include "../Fonts.hpp"
#include <cstring>
#include <cstdio>

PowerNodeWidget::PowerNodeWidget(const WidgetConfig& config)
    : Widget(config)
{
    labelText_[0] = '\0';
    valueFormat_[0] = '\0';
    leftFormat_[0] = '\0';
    rightFormat_[0] = '\0';
}

static bool set_label_text_if_changed(lv_obj_t* label, const char* text) {
    if (!label || !text) return false;
    const char* current = lv_label_get_text(label);
    if (current && strcmp(current, text) == 0) return false;
    lv_label_set_text(label, text);
    return true;
}

static void set_obj_name(lv_obj_t* obj, const char* base, uint8_t id, const char* suffix) {
#if LV_USE_OBJ_NAME
    if (!obj || !base) return;
    char name[48];
    if (suffix && suffix[0] != '\0') {
        snprintf(name, sizeof(name), "%s#%u_%s", base, id, suffix);
    } else {
        snprintf(name, sizeof(name), "%s#%u", base, id);
    }
    lv_obj_set_name(obj, name);
#else
    (void)obj;
    (void)base;
    (void)id;
    (void)suffix;
#endif
}

int PowerNodeWidget::encodeUtf8(uint32_t codepoint, char* buf) {
    if (codepoint < 0x80) {
        buf[0] = static_cast<char>(codepoint);
        buf[1] = '\0';
        return 1;
    } else if (codepoint < 0x800) {
        buf[0] = static_cast<char>(0xC0 | (codepoint >> 6));
        buf[1] = static_cast<char>(0x80 | (codepoint & 0x3F));
        buf[2] = '\0';
        return 2;
    } else if (codepoint < 0x10000) {
        buf[0] = static_cast<char>(0xE0 | (codepoint >> 12));
        buf[1] = static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
        buf[2] = static_cast<char>(0x80 | (codepoint & 0x3F));
        buf[3] = '\0';
        return 3;
    } else if (codepoint < 0x110000) {
        buf[0] = static_cast<char>(0xF0 | (codepoint >> 18));
        buf[1] = static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F));
        buf[2] = static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
        buf[3] = static_cast<char>(0x80 | (codepoint & 0x3F));
        buf[4] = '\0';
        return 4;
    }
    buf[0] = '\0';
    return 0;
}

void PowerNodeWidget::parseText() {
    labelText_[0] = '\0';
    valueFormat_[0] = '\0';
    leftFormat_[0] = '\0';
    rightFormat_[0] = '\0';

    // Parse primary text (label\nformat)
    const char* text = config_.text;
    if (text && text[0] != '\0') {
        const char* newline = strchr(text, '\n');
        if (newline) {
            size_t labelLen = static_cast<size_t>(newline - text);
            if (labelLen >= MAX_TEXT_LEN) labelLen = MAX_TEXT_LEN - 1;
            memcpy(labelText_, text, labelLen);
            labelText_[labelLen] = '\0';
            strncpy(valueFormat_, newline + 1, MAX_TEXT_LEN - 1);
            valueFormat_[MAX_TEXT_LEN - 1] = '\0';
        } else {
            strncpy(labelText_, text, MAX_TEXT_LEN - 1);
            labelText_[MAX_TEXT_LEN - 1] = '\0';
        }
    }

    // Copy left format from text2
    if (config_.text2[0] != '\0') {
        strncpy(leftFormat_, config_.text2, MAX_FORMAT_LEN - 1);
        leftFormat_[MAX_FORMAT_LEN - 1] = '\0';
    }

    // Copy right format from text3
    if (config_.text3[0] != '\0') {
        strncpy(rightFormat_, config_.text3, MAX_FORMAT_LEN - 1);
        rightFormat_[MAX_FORMAT_LEN - 1] = '\0';
    }
}

lv_obj_t* PowerNodeWidget::create(lv_obj_t* parent) {
    parseText();

    obj_ = lv_obj_create(parent);
    if (obj_ == nullptr) {
        return nullptr;
    }
    set_obj_name(obj_, "PowerNode", config_.id, nullptr);

    lv_obj_remove_style_all(obj_);
    lv_obj_set_pos(obj_, config_.x, config_.y);
    lv_obj_set_size(obj_,
        config_.width > 0 ? config_.width : 120,
        config_.height > 0 ? config_.height : 120);
    lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_flex_flow(obj_, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(obj_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_all(obj_, 6, 0);
    lv_obj_set_style_pad_gap(obj_, 2, 0);

    // Top label (title)
    if (labelText_[0] != '\0') {
        labelLabel_ = lv_label_create(obj_);
        set_obj_name(labelLabel_, "PowerNode", config_.id, "label");
        lv_label_set_text(labelLabel_, labelText_);
        lv_obj_clear_flag(labelLabel_, LV_OBJ_FLAG_CLICKABLE);
    }

    // Check if we have left/right values (dual-value mode)
    bool hasDualValues = (config_.knxAddress2 > 0 || config_.knxAddress3 > 0);

    if (hasDualValues) {
        // Create middle row container for: left | icon | right
        middleRow_ = lv_obj_create(obj_);
        lv_obj_remove_style_all(middleRow_);
        set_obj_name(middleRow_, "PowerNode", config_.id, "middleRow");
        lv_obj_set_size(middleRow_, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
        lv_obj_clear_flag(middleRow_, LV_OBJ_FLAG_SCROLLABLE);
        lv_obj_set_flex_flow(middleRow_, LV_FLEX_FLOW_ROW);
        lv_obj_set_flex_align(middleRow_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
        lv_obj_set_style_pad_gap(middleRow_, 8, 0);

        // Left value label (secondary)
        if (config_.knxAddress2 > 0) {
            leftLabel_ = lv_label_create(middleRow_);
            set_obj_name(leftLabel_, "PowerNode", config_.id, "left");
            lv_label_set_text(leftLabel_, leftFormat_[0] != '\0' ? leftFormat_ : "");
            lv_obj_clear_flag(leftLabel_, LV_OBJ_FLAG_CLICKABLE);
        }

        // Center icon
        if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) {
            iconLabel_ = lv_label_create(middleRow_);
            set_obj_name(iconLabel_, "PowerNode", config_.id, "icon");
            char iconText[5];
            encodeUtf8(config_.iconCodepoint, iconText);
            lv_label_set_text(iconLabel_, iconText);
            lv_obj_clear_flag(iconLabel_, LV_OBJ_FLAG_CLICKABLE);
        }

        // Right value label (tertiary)
        if (config_.knxAddress3 > 0) {
            rightLabel_ = lv_label_create(middleRow_);
            set_obj_name(rightLabel_, "PowerNode", config_.id, "right");
            lv_label_set_text(rightLabel_, rightFormat_[0] != '\0' ? rightFormat_ : "");
            lv_obj_clear_flag(rightLabel_, LV_OBJ_FLAG_CLICKABLE);
        }
    } else {
        // Original layout: icon in column flow
        if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) {
            iconLabel_ = lv_label_create(obj_);
            set_obj_name(iconLabel_, "PowerNode", config_.id, "icon");
            char iconText[5];
            encodeUtf8(config_.iconCodepoint, iconText);
            lv_label_set_text(iconLabel_, iconText);
            lv_obj_clear_flag(iconLabel_, LV_OBJ_FLAG_CLICKABLE);
        }
    }

    // Bottom value label (primary)
    valueLabel_ = lv_label_create(obj_);
    set_obj_name(valueLabel_, "PowerNode", config_.id, "value");
    if (valueFormat_[0] != '\0') {
        lv_label_set_text(valueLabel_, valueFormat_);
    } else {
        lv_label_set_text(valueLabel_, "");
    }
    lv_obj_clear_flag(valueLabel_, LV_OBJ_FLAG_CLICKABLE);

    return obj_;
}

void PowerNodeWidget::applyStyle() {
    if (obj_ == nullptr) return;

    lv_obj_set_style_bg_color(obj_, lv_color_white(), 0);
    lv_obj_set_style_bg_opa(obj_, LV_OPA_COVER, 0);
    lv_obj_set_style_radius(obj_, LV_RADIUS_CIRCLE, 0);

    int16_t minSide = config_.width < config_.height ? config_.width : config_.height;
    int16_t ring = minSide / 12;
    if (ring < 2) ring = 2;
    if (ring > 12) ring = 12;

    if (config_.borderWidth > 0 && config_.borderOpacity > 0) {
        lv_obj_set_style_border_width(obj_, config_.borderWidth, 0);
        lv_obj_set_style_border_color(obj_, lv_color_make(
            config_.borderColor.r, config_.borderColor.g, config_.borderColor.b), 0);
        lv_obj_set_style_border_opa(obj_, config_.borderOpacity, 0);
    } else {
        lv_obj_set_style_border_width(obj_, ring, 0);
        lv_obj_set_style_border_color(obj_, lv_color_make(
            config_.bgColor.r, config_.bgColor.g, config_.bgColor.b), 0);
        lv_obj_set_style_border_opa(obj_, config_.bgOpacity, 0);
    }

    applyShadowStyle();

    uint8_t valueSizeIdx = config_.fontSize;
    uint8_t labelSizeIdx = valueSizeIdx > 0 ? static_cast<uint8_t>(valueSizeIdx - 1) : valueSizeIdx;
    uint8_t sideValueSizeIdx = labelSizeIdx; // Left/right values use smaller font

    if (labelLabel_ != nullptr) {
        lv_obj_set_style_text_color(labelLabel_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        lv_obj_set_style_text_font(labelLabel_, Fonts::bySizeIndex(labelSizeIdx), 0);
        lv_obj_set_style_text_align(labelLabel_, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_opa(labelLabel_, LV_OPA_70, 0);
    }

    if (iconLabel_ != nullptr) {
        lv_obj_set_style_text_color(iconLabel_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        uint8_t sizeIdx = config_.iconSize < 6 ? config_.iconSize : valueSizeIdx;
        lv_obj_set_style_text_font(iconLabel_, Fonts::iconFont(sizeIdx), 0);
        lv_obj_set_style_text_align(iconLabel_, LV_TEXT_ALIGN_CENTER, 0);
    }

    if (leftLabel_ != nullptr) {
        lv_obj_set_style_text_color(leftLabel_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        lv_obj_set_style_text_font(leftLabel_, Fonts::bySizeIndex(sideValueSizeIdx), 0);
        lv_obj_set_style_text_align(leftLabel_, LV_TEXT_ALIGN_RIGHT, 0);
    }

    if (rightLabel_ != nullptr) {
        lv_obj_set_style_text_color(rightLabel_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        lv_obj_set_style_text_font(rightLabel_, Fonts::bySizeIndex(sideValueSizeIdx), 0);
        lv_obj_set_style_text_align(rightLabel_, LV_TEXT_ALIGN_LEFT, 0);
    }

    if (valueLabel_ != nullptr) {
        lv_obj_set_style_text_color(valueLabel_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        lv_obj_set_style_text_font(valueLabel_, Fonts::bySizeIndex(valueSizeIdx), 0);
        lv_obj_set_style_text_align(valueLabel_, LV_TEXT_ALIGN_CENTER, 0);
    }
}

void PowerNodeWidget::updateValueText(const char* text) {
    if (valueLabel_ == nullptr || text == nullptr) return;
    set_label_text_if_changed(valueLabel_, text);
}

void PowerNodeWidget::updateLeftText(const char* text) {
    if (leftLabel_ == nullptr || text == nullptr) return;
    set_label_text_if_changed(leftLabel_, text);
}

void PowerNodeWidget::updateRightText(const char* text) {
    if (rightLabel_ == nullptr || text == nullptr) return;
    set_label_text_if_changed(rightLabel_, text);
}

void PowerNodeWidget::updateIcon(uint32_t codepoint) {
    if (iconLabel_ == nullptr || codepoint == 0) return;
    char iconText[5];
    encodeUtf8(codepoint, iconText);
    set_label_text_if_changed(iconLabel_, iconText);
}

static bool isNumericSource(TextSource source) {
    return source == TextSource::KNX_DPT_TEMP ||
           source == TextSource::KNX_DPT_PERCENT ||
           source == TextSource::KNX_DPT_POWER ||
           source == TextSource::KNX_DPT_ENERGY ||
           source == TextSource::KNX_DPT_DECIMALFACTOR;
}

static void formatValue(char* buf, size_t bufSize, float value, const char* fmt, TextSource source) {
    const char* useFmt = (fmt && fmt[0] != '\0') ? fmt : "%0.1f";
    if (source == TextSource::KNX_DPT_PERCENT ||
        source == TextSource::KNX_DPT_DECIMALFACTOR) {
        int intVal = static_cast<int>(value + 0.5f);
        snprintf(buf, bufSize, useFmt, intVal);
    } else {
        snprintf(buf, bufSize, useFmt, value);
    }
}

void PowerNodeWidget::onKnxValue(float value) {
    // Cache and call base for condition evaluation
    cachedPrimaryValue_ = value;
    hasCachedPrimary_ = true;

    if (valueLabel_ == nullptr) return;
    if (!isNumericSource(config_.textSource)) return;

    char buf[32];
    formatValue(buf, sizeof(buf), value, valueFormat_, config_.textSource);
    updateValueText(buf);

    // Evaluate conditions after updating value
    evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
}

void PowerNodeWidget::onKnxValue2(float value) {
    cachedSecondaryValue_ = value;
    hasCachedSecondary_ = true;

    if (leftLabel_ == nullptr) return;
    if (!isNumericSource(config_.textSource2)) return;

    char buf[32];
    formatValue(buf, sizeof(buf), value, leftFormat_, config_.textSource2);
    updateLeftText(buf);

    // Evaluate conditions after updating value
    evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
}

void PowerNodeWidget::onKnxValue3(float value) {
    cachedTertiaryValue_ = value;
    hasCachedTertiary_ = true;

    if (rightLabel_ == nullptr) return;
    if (!isNumericSource(config_.textSource3)) return;

    char buf[32];
    formatValue(buf, sizeof(buf), value, rightFormat_, config_.textSource3);
    updateRightText(buf);

    // Evaluate conditions after updating value
    evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
}

void PowerNodeWidget::onKnxSwitch(bool value) {
    if (valueLabel_ == nullptr) return;
    if (config_.textSource != TextSource::KNX_DPT_SWITCH) return;
    updateValueText(value ? "EIN" : "AUS");
}

void PowerNodeWidget::onKnxText(const char* text) {
    if (valueLabel_ == nullptr) return;
    if (config_.textSource != TextSource::KNX_DPT_TEXT) return;
    updateValueText(text);
}

bool PowerNodeWidget::evaluateConditions(float primaryValue, float secondaryValue, float tertiaryValue) {
    if (config_.conditionCount == 0) return false;

    const StyleCondition* bestMatch = nullptr;
    uint8_t bestPriority = 255;

    for (uint8_t i = 0; i < config_.conditionCount && i < MAX_CONDITIONS; ++i) {
        const StyleCondition& cond = config_.conditions[i];
        if (!cond.enabled) continue;

        // Get the value to check based on source
        float checkValue = 0.0f;
        bool hasValue = false;
        switch (cond.source) {
            case ConditionSource::PRIMARY:
                checkValue = primaryValue;
                hasValue = hasCachedPrimary_;
                break;
            case ConditionSource::SECONDARY:
                checkValue = secondaryValue;
                hasValue = hasCachedSecondary_;
                break;
            case ConditionSource::TERTIARY:
                checkValue = tertiaryValue;
                hasValue = hasCachedTertiary_;
                break;
        }

        if (!hasValue) continue;

        // Evaluate condition
        bool matches = false;
        switch (cond.op) {
            case ConditionOp::LESS:
                matches = checkValue < cond.threshold;
                break;
            case ConditionOp::LESS_EQUAL:
                matches = checkValue <= cond.threshold;
                break;
            case ConditionOp::EQUAL:
                matches = checkValue == cond.threshold;
                break;
            case ConditionOp::GREATER_EQUAL:
                matches = checkValue >= cond.threshold;
                break;
            case ConditionOp::GREATER:
                matches = checkValue > cond.threshold;
                break;
            case ConditionOp::NOT_EQUAL:
                matches = checkValue != cond.threshold;
                break;
        }

        if (matches && cond.priority < bestPriority) {
            bestMatch = &cond;
            bestPriority = cond.priority;
        }
    }

    if (bestMatch) {
        // Apply icon change
        if (bestMatch->style.iconCodepoint != 0 &&
            bestMatch->style.iconCodepoint != currentConditionIcon_) {
            updateIcon(bestMatch->style.iconCodepoint);
            currentConditionIcon_ = bestMatch->style.iconCodepoint;
        }

        // Apply text color to icon
        if ((bestMatch->style.flags & ConditionStyle::FLAG_USE_TEXT_COLOR) && iconLabel_) {
            lv_obj_set_style_text_color(iconLabel_, lv_color_make(
                bestMatch->style.textColor.r,
                bestMatch->style.textColor.g,
                bestMatch->style.textColor.b), 0);
        }

        // Apply border color change (bgColor affects border in PowerNode)
        if (bestMatch->style.flags & ConditionStyle::FLAG_USE_BG_COLOR) {
            lv_obj_set_style_border_color(obj_, lv_color_make(
                bestMatch->style.bgColor.r,
                bestMatch->style.bgColor.g,
                bestMatch->style.bgColor.b), 0);
        }

        // Handle hide flag
        if (bestMatch->style.flags & ConditionStyle::FLAG_HIDE) {
            lv_obj_add_flag(obj_, LV_OBJ_FLAG_HIDDEN);
        } else {
            lv_obj_clear_flag(obj_, LV_OBJ_FLAG_HIDDEN);
        }

        return true;
    }

    return false;
}
