//
// Created by Lenn on 2025/10/17.
//

#ifndef TOUCHSENSOR_HEATMAP_IMPL_HH
#define TOUCHSENSOR_HEATMAP_IMPL_HH

#include "heatmap.hh"
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/widget/sliders.hh"
#include "qcustomplot/qcustomplot.h"
#include <algorithm>
#include <memory>
#include <qcolor.h>
#include <qdebug.h>
#include <qfont.h>
#include <vector>
using namespace creeper::plot_widget::internal;

struct BasicPlot::Impl {
    explicit Impl(BasicPlot& self) noexcept : self{self}, initialized(false), matrix_size(QSize{3, 4}) {}

public:
    auto set_xlabel_text(const QString& text) -> void {
        xlabel = text;
        if (initialized) {
            self.xAxis->setLabel(text);
            self.replot();
        }
    }

    auto set_ylabel_text(const QString& text) -> void {
        ylabel = text;
        if (initialized) {
            self.yAxis->setLabel(text);
            self.replot();
        }
    }

    auto set_matrix_size(const QSize& size) -> void {
        matrix_size = size;
        if (initialized) {
            reset_plot();
            if (!data_points.isEmpty()) {
                set_data(data_points);
            }
        }
    }

    auto load_theme_manager(ThemeManager& mgr) -> void {
        mgr.append_handler(&self, [this](const ThemeManager& mgr) {
            if (initialized) {
                self.replot();
            }
        });
    }

    auto set_color_gradient_range(const double& min, const double& max) -> void {
        if (initialized && color_map) {
            color_map->setDataRange(QCPRange(min, max));
            self.replot();
        }
        color_min = min;
        color_max = max;
    }

    auto set_data(const QVector<PointData>& data) -> void {
        data_points = data;
        if (initialized && color_map) {
            update_plot_data();
        }
    }

    auto initialize_plot() -> void {
        if (initialized) return;

        color_map = new QCPColorMap(self.xAxis, self.yAxis);
        auto* cpmp = color_map;
        cpmp->data()->setSize(matrix_size.width(), matrix_size.height());
        cpmp->data()->setRange(QCPRange(0.5, matrix_size.width() - 0.5), 
                             QCPRange(0.5, matrix_size.height() - 0.5));
        
        QSharedPointer<QCPAxisTickerText> xticker(new QCPAxisTickerText);
        QSharedPointer<QCPAxisTickerText> yticker(new QCPAxisTickerText);
        xticker->setSubTickCount(1);
        yticker->setSubTickCount(1);
        self.xAxis->setVisible(false);
        self.yAxis->setVisible(false);
        self.xAxis->setTicker(xticker);
        self.yAxis->setTicker(yticker);

        self.xAxis->grid()->setPen(Qt::NoPen);
        self.yAxis->grid()->setPen(Qt::NoPen);
        self.xAxis->grid()->setSubGridVisible(true);
        self.yAxis->grid()->setSubGridVisible(true);
        self.xAxis->setSubTicks(true);
        self.yAxis->setSubTicks(true);
        self.xAxis->setTickLength(0);
        self.yAxis->setTickLength(0);
        self.xAxis->setSubTickLength(6);
        self.yAxis->setSubTickLength(6);
        

        self.xAxis->setRange(0, matrix_size.width());
        self.yAxis->setRange(0, matrix_size.height());

        if (!xlabel.isEmpty()) self.xAxis->setLabel(xlabel);
        if (!ylabel.isEmpty()) self.yAxis->setLabel(ylabel);
        
        if (!color_scale) {
            color_scale = new QCPColorScale(&self);
            color_scale->setType(QCPAxis::atBottom);
        }
        if (self.plotLayout()) {
            auto existing = self.plotLayout()->element(1, 0);
            if (existing && existing != color_scale) {
                self.plotLayout()->take(existing);
            }
            if (!existing || existing != color_scale) {
                self.plotLayout()->addElement(1, 0, color_scale);
            }
        }
        cpmp->setColorScale(color_scale);

        QCPColorGradient gradient;
        gradient.setColorStopAt(0.0, QColor(240, 246, 255)); // 低值淡色
        gradient.setColorStopAt(0.35, QColor(142, 197, 252));
        gradient.setColorStopAt(0.7, QColor(56, 128, 199));
        gradient.setColorStopAt(1.0, QColor(8, 36, 95));     // 高值深色
        cpmp->setGradient(gradient);

        cpmp->setDataRange(QCPRange(color_min, color_max));
        cpmp->setInterpolate(false);
        
        QCPMarginGroup *margin_group = new QCPMarginGroup(&self);
        self.axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
        color_scale->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
        
        initialized = true;
        
        if (!data_points.isEmpty()) {
            update_plot_data();
        }
    }

    auto reset_plot() -> void {
        // 清除所有绘图元素
        self.clearPlottables();
        self.clearGraphs();
        self.clearItems();
        self.clearFocus();
        color_map = nullptr;
        cell_labels.clear();
        
        // 重新初始化
        initialized = false;
        initialize_plot();
    }
    auto update_plot_data() -> void {
        if (!initialized || !color_map) return;

        ensure_labels();

        const int width = matrix_size.width();
        const int height = matrix_size.height();
        const int expected = width * height;
        std::vector<double> values(static_cast<std::size_t>(expected), 0.0);

        // 设置新数据
        for (const auto& item : data_points) {
            if (item.x >= 0 && item.x < matrix_size.width() && 
                item.y >= 0 && item.y < matrix_size.height()) {
                color_map->data()->setCell(item.x, item.y, item.z);
                const int idx = static_cast<int>(item.y) * width + static_cast<int>(item.x);
                if (idx >= 0 && idx < expected) {
                    values[static_cast<std::size_t>(idx)] = item.z;
                }
            }
        }

        update_label_values(values);
        
        // 重绘
        self.replot();
    }

    auto is_plot_initialized() const -> bool {
        return initialized;
    }

    auto get_matrix_size() const -> QSize {
        return matrix_size;
    }

private:
    QString xlabel;
    QString ylabel;
    QSize matrix_size;
    QVector<PointData> data_points;
    double color_min = 0.0;
    double color_max = 800.0;
    bool initialized;
    BasicPlot& self;
    QCPColorScale* color_scale = nullptr;
    QCPColorMap* color_map = nullptr;
    QVector<QCPItemText*> cell_labels;

    void clear_labels() {
        for (auto* label : cell_labels) {
            if (label) {
                self.removeItem(label);
            }
        }
        cell_labels.clear();
    }

    void ensure_labels() {
        const int width = matrix_size.width();
        const int height = matrix_size.height();
        const int expected = width * height;
        if (expected <= 0) {
            clear_labels();
            return;
        }
        if (cell_labels.size() == expected) {
            return;
        }

        clear_labels();
        cell_labels.reserve(expected);
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                auto* label = new QCPItemText(&self);
                label->position->setType(QCPItemPosition::ptPlotCoords);
                label->setClipToAxisRect(true);
                label->setClipAxisRect(self.axisRect());
                label->setPositionAlignment(Qt::AlignCenter);
                label->position->setCoords(x + 0.5, y + 0.5);
                label->setBrush(Qt::NoBrush);
                label->setPen(Qt::NoPen);
                QFont font = label->font();
                if (font.pointSize() > 0) {
                    font.setPointSize(std::max(font.pointSize() - 1, 6));
                } else {
                    font.setPointSize(8);
                }
                label->setFont(font);
                label->setColor(Qt::black);
                label->setSelectable(false);
                cell_labels.push_back(label);
            }
        }
    }

    void update_label_values(const std::vector<double>& values) {
        const int width = matrix_size.width();
        const int height = matrix_size.height();
        const double range = std::max(color_max - color_min, 1.0);
        const int expected = width * height;
        for (int idx = 0; idx < expected && idx < cell_labels.size(); ++idx) {
            auto* label = cell_labels[idx];
            if (!label) {
                continue;
            }
            const double value = values.size() > static_cast<std::size_t>(idx)
                                     ? values[static_cast<std::size_t>(idx)]
                                     : 0.0;
            label->setText(QString::number(value, 'f', 0));
            const double normalized = std::clamp((value - color_min) / range, 0.0, 1.0);
            if (normalized > 0.55) {
                label->setColor(Qt::white);
            } else {
                label->setColor(QColor(25, 25, 25));
            }
            const int x = idx % width;
            const int y = idx / width;
            label->position->setCoords(x + 0.5, y + 0.5);
        }
    }
};

#endif // TOUCHSENSOR_HEATMAP_IMPL_HH
