#include "ChartWidget.hpp"
#include "../Fonts.hpp"
#include "lvgl.h"
#include <algorithm>

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

lv_obj_t* ChartWidget::create(lv_obj_t* parent) {
    obj_ = lv_obj_create(parent);
    if (!obj_) return nullptr;
    lv_obj_remove_style_all(obj_);

    int32_t width = config_.width > 0 ? config_.width : 240;
    int32_t height = config_.height > 0 ? config_.height : 160;
    lv_obj_set_pos(obj_, config_.x, config_.y);
    lv_obj_set_size(obj_, width, height);
    lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);

    const int32_t yAxisWidth = 48;
    const int32_t xAxisHeight = 26;
    int32_t chartWidth = width - yAxisWidth;
    int32_t chartHeight = height - xAxisHeight;
    if (chartWidth < 20) chartWidth = 20;
    if (chartHeight < 20) chartHeight = 20;

    yScale_ = lv_scale_create(obj_);
    if (yScale_) {
        lv_scale_set_mode(yScale_, LV_SCALE_MODE_VERTICAL_LEFT);
        lv_scale_set_total_tick_count(yScale_, 5);
        lv_scale_set_major_tick_every(yScale_, 1);
        lv_scale_set_label_show(yScale_, true);
        lv_scale_set_range(yScale_, 0, 100);
        lv_obj_set_pos(yScale_, 0, 0);
        lv_obj_set_size(yScale_, yAxisWidth, chartHeight);
    }

    xScale_ = lv_scale_create(obj_);
    if (xScale_) {
        lv_scale_set_mode(xScale_, LV_SCALE_MODE_HORIZONTAL_BOTTOM);
        lv_scale_set_label_show(xScale_, true);
        lv_obj_set_pos(xScale_, yAxisWidth, chartHeight);
        lv_obj_set_size(xScale_, chartWidth, xAxisHeight);
    }

    chart_ = lv_chart_create(obj_);
    if (!chart_) {
        return obj_;
    }
    lv_obj_set_pos(chart_, yAxisWidth, 0);
    lv_obj_set_size(chart_, chartWidth, chartHeight);
    lv_obj_clear_flag(chart_, LV_OBJ_FLAG_SCROLLABLE);

    lv_chart_set_type(chart_, LV_CHART_TYPE_LINE);
    lv_chart_set_point_count(chart_, HistoryStore::CHART_POINT_COUNT);
    lv_chart_set_div_line_count(chart_, 4, 6);

    uint8_t count = config_.chartSeriesCount;
    if (count > CHART_MAX_SERIES) count = CHART_MAX_SERIES;

    for (uint8_t i = 0; i < count; ++i) {
        lv_color_t color = lv_color_make(
            config_.chartSeriesColor[i].r,
            config_.chartSeriesColor[i].g,
            config_.chartSeriesColor[i].b);
        series_[i] = lv_chart_add_series(chart_, color, LV_CHART_AXIS_PRIMARY_Y);
        if (series_[i]) {
            lv_chart_set_series_ext_y_array(chart_, series_[i], seriesData_[i].data());
            std::fill(seriesData_[i].begin(), seriesData_[i].end(), HistoryStore::NO_POINT);
        }
    }

    applyAxisLabels();
    refreshData();
    return obj_;
}

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

    Widget::applyStyle();
    lv_obj_set_style_pad_all(obj_, 0, 0);

    if (chart_) {
        lv_obj_set_style_border_width(chart_, 0, 0);
        lv_obj_set_style_pad_all(chart_, 6, 0);
        lv_obj_set_style_line_width(chart_, 2, LV_PART_ITEMS);
    }

    const lv_color_t textColor = lv_color_make(
        config_.textColor.r, config_.textColor.g, config_.textColor.b);
    const lv_font_t* axisFont = Fonts::bySizeIndex(0);
    if (yScale_) {
        lv_obj_set_style_text_color(yScale_, textColor, LV_PART_INDICATOR);
        lv_obj_set_style_text_font(yScale_, axisFont, LV_PART_INDICATOR);
        lv_obj_set_style_bg_opa(yScale_, LV_OPA_TRANSP, 0);
        lv_obj_set_style_border_width(yScale_, 0, 0);
    }
    if (xScale_) {
        lv_obj_set_style_text_color(xScale_, textColor, LV_PART_INDICATOR);
        lv_obj_set_style_text_font(xScale_, axisFont, LV_PART_INDICATOR);
        lv_obj_set_style_bg_opa(xScale_, LV_OPA_TRANSP, 0);
        lv_obj_set_style_border_width(xScale_, 0, 0);
    }
}

void ChartWidget::onHistoryUpdate() {
    refreshData();
}

void ChartWidget::refreshData() {
    if (!chart_) return;

    uint8_t count = config_.chartSeriesCount;
    if (count > CHART_MAX_SERIES) count = CHART_MAX_SERIES;

    bool hasAny = false;
    int32_t globalMin = 0;
    int32_t globalMax = 0;

    for (uint8_t i = 0; i < count; ++i) {
        if (!series_[i]) continue;

        HistoryStore::instance().fillChartSeries(
            config_.chartKnxAddress[i],
            config_.chartTextSource[i],
            static_cast<ChartPeriod>(config_.chartPeriod),
            seriesData_[i].data(),
            seriesData_[i].size());

        for (size_t j = 0; j < seriesData_[i].size(); ++j) {
            int32_t value = seriesData_[i][j];
            if (value == HistoryStore::NO_POINT) continue;
            if (!hasAny) {
                globalMin = value;
                globalMax = value;
                hasAny = true;
            } else {
                globalMin = std::min(globalMin, value);
                globalMax = std::max(globalMax, value);
            }
        }
    }

    if (hasAny) {
        int32_t range = globalMax - globalMin;
        if (range < 1) range = 1;
        int32_t pad = range / 10;
        if (pad < 1) pad = 1;
        int32_t minVal = globalMin - pad;
        int32_t maxVal = globalMax + pad;
        lv_chart_set_axis_range(chart_, LV_CHART_AXIS_PRIMARY_Y, minVal, maxVal);
        if (yScale_) {
            lv_scale_set_range(yScale_, minVal, maxVal);
        }
    } else {
        lv_chart_set_axis_range(chart_, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
        if (yScale_) {
            lv_scale_set_range(yScale_, 0, 100);
        }
    }

    lv_chart_refresh(chart_);
}

const char** ChartWidget::labelsForPeriod(ChartPeriod period, uint8_t* count) {
    static const char* k1h[] = { "-60m", "-45m", "-30m", "-15m", "0", nullptr };
    static const char* k3h[] = { "-3h", "-2h", "-1h", "-30m", "0", nullptr };
    static const char* k5h[] = { "-5h", "-4h", "-3h", "-2h", "-1h", "0", nullptr };
    static const char* k12h[] = { "-12h", "-9h", "-6h", "-3h", "0", nullptr };
    static const char* k24h[] = { "-24h", "-18h", "-12h", "-6h", "0", nullptr };
    static const char* k1m[] = { "-30d", "-21d", "-14d", "-7d", "0", nullptr };

    switch (period) {
        case ChartPeriod::HOUR_1:
            if (count) *count = 5;
            return k1h;
        case ChartPeriod::HOUR_3:
            if (count) *count = 5;
            return k3h;
        case ChartPeriod::HOUR_5:
            if (count) *count = 6;
            return k5h;
        case ChartPeriod::HOUR_12:
            if (count) *count = 5;
            return k12h;
        case ChartPeriod::HOUR_24:
            if (count) *count = 5;
            return k24h;
        case ChartPeriod::MONTH_1:
            if (count) *count = 5;
            return k1m;
        default:
            if (count) *count = 5;
            return k1h;
    }
}

void ChartWidget::applyAxisLabels() {
    if (!xScale_) return;
    uint8_t count = 5;
    const char** labels = labelsForPeriod(static_cast<ChartPeriod>(config_.chartPeriod), &count);
    lv_scale_set_total_tick_count(xScale_, count);
    lv_scale_set_major_tick_every(xScale_, 1);
    lv_scale_set_text_src(xScale_, labels);
}
