#include "ButtonMatrixWidget.hpp"

#include "../WidgetManager.hpp"

#include <algorithm>
#include <cctype>

namespace {
std::string trimToken(const std::string& token) {
    size_t start = 0;
    size_t end = token.size();

    while (start < end && std::isspace(static_cast<unsigned char>(token[start])) != 0) {
        start++;
    }
    while (end > start && std::isspace(static_cast<unsigned char>(token[end - 1])) != 0) {
        end--;
    }

    return token.substr(start, end - start);
}
}

ButtonMatrixWidget::ButtonMatrixWidget(const WidgetConfig& config)
    : Widget(config)
{
}

ButtonMatrixWidget::~ButtonMatrixWidget() {
    if (obj_) {
        lv_obj_remove_event_cb(obj_, valueChangedCallback);
    }
}

void ButtonMatrixWidget::rebuildMapStorage() {
    mapStorage_.clear();
    mapPointers_.clear();

    auto addButton = [&](const std::string& token) {
        std::string trimmed = trimToken(token);
        if (!trimmed.empty()) {
            mapStorage_.push_back(trimmed);
        }
    };

    if (config_.text[0] != '\0') {
        std::string token;
        for (size_t i = 0; config_.text[i] != '\0'; ++i) {
            const char ch = config_.text[i];
            if (ch == ';' || ch == ',') {
                addButton(token);
                token.clear();
            } else if (ch == '\n' || ch == '\r') {
                addButton(token);
                token.clear();
                if (!mapStorage_.empty() && mapStorage_.back() != "\n") {
                    mapStorage_.push_back("\n");
                }
                if (ch == '\r' && config_.text[i + 1] == '\n') {
                    ++i;
                }
            } else {
                token.push_back(ch);
            }
        }
        addButton(token);

        while (!mapStorage_.empty() && mapStorage_.back() == "\n") {
            mapStorage_.pop_back();
        }
    }

    if (mapStorage_.empty()) {
        mapStorage_ = {"1", "2", "3", "\n", "4", "5", "6", "\n", "7", "8", "9"};
    }

    mapPointers_.reserve(mapStorage_.size() + 1);
    for (const std::string& entry : mapStorage_) {
        mapPointers_.push_back(entry.c_str());
    }
    mapPointers_.push_back("");
}

lv_obj_t* ButtonMatrixWidget::create(lv_obj_t* parent) {
    obj_ = lv_buttonmatrix_create(parent);
    if (!obj_) return nullptr;

    lv_obj_set_pos(obj_, config_.x, config_.y);
    lv_obj_set_size(obj_,
                    config_.width > 0 ? config_.width : 220,
                    config_.height > 0 ? config_.height : 140);
    lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);

    rebuildMapStorage();
    lv_buttonmatrix_set_map(obj_, mapPointers_.data());
    lv_buttonmatrix_set_button_ctrl_all(obj_, LV_BUTTONMATRIX_CTRL_CLICK_TRIG);

    if (config_.isToggle) {
        lv_buttonmatrix_set_button_ctrl_all(obj_, LV_BUTTONMATRIX_CTRL_CHECKABLE);
    }

    lv_obj_add_event_cb(obj_, valueChangedCallback, LV_EVENT_VALUE_CHANGED, this);

    return obj_;
}

void ButtonMatrixWidget::applyStyle() {
    if (!obj_) return;

    applyCommonStyle();

    lv_color_t textColor = lv_color_make(
        config_.textColor.r, config_.textColor.g, config_.textColor.b);
    lv_color_t buttonColor = lv_color_make(
        config_.bgColor.r, config_.bgColor.g, config_.bgColor.b);

    uint8_t buttonOpa = config_.bgOpacity > 0 ? config_.bgOpacity : static_cast<uint8_t>(LV_OPA_30);

    lv_obj_set_style_text_font(obj_, getFontBySize(config_.fontSize), LV_PART_ITEMS);
    lv_obj_set_style_text_color(obj_, textColor, LV_PART_ITEMS);
    lv_obj_set_style_bg_color(obj_, buttonColor, LV_PART_ITEMS);
    lv_obj_set_style_bg_opa(obj_, buttonOpa, LV_PART_ITEMS);

    if (config_.borderRadius > 0) {
        lv_obj_set_style_radius(obj_, config_.borderRadius, LV_PART_ITEMS);
    }

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

    lv_style_selector_t pressedSel = static_cast<lv_style_selector_t>(LV_PART_ITEMS) |
                                     static_cast<lv_style_selector_t>(LV_STATE_PRESSED);
    lv_style_selector_t checkedSel = static_cast<lv_style_selector_t>(LV_PART_ITEMS) |
                                     static_cast<lv_style_selector_t>(LV_STATE_CHECKED);

    lv_obj_set_style_bg_color(obj_, textColor, pressedSel);
    lv_obj_set_style_bg_opa(obj_, LV_OPA_40, pressedSel);
    lv_obj_set_style_text_color(obj_, lv_color_white(), pressedSel);

    lv_obj_set_style_bg_color(obj_, textColor, checkedSel);
    lv_obj_set_style_bg_opa(obj_, LV_OPA_50, checkedSel);
    lv_obj_set_style_text_color(obj_, lv_color_white(), checkedSel);
}

void ButtonMatrixWidget::valueChangedCallback(lv_event_t* e) {
    ButtonMatrixWidget* widget = static_cast<ButtonMatrixWidget*>(lv_event_get_user_data(e));
    if (!widget) return;

    lv_obj_t* target = static_cast<lv_obj_t*>(lv_event_get_target(e));
    WidgetManager::instance().handleButtonAction(widget->getConfig(), target);
}
