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

#ifndef TOUCHSENSOR_HEATMAP_H
#define TOUCHSENSOR_HEATMAP_H

#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "qcustomplot/qcustomplot.h"
#include "modern-qt/utility/wrapper/widget.hh"
#include <concepts>
#include <qcontainerfwd.h>
#include <qvector.h>

struct point_data {
    double x;
    double y;
    double z;
    explicit point_data(double x, double y, double z) : x{x}, y{y}, z{z} {}
};
using PointData = struct point_data;
namespace creeper {
class HeatMapPlot;

namespace plot_widget::internal {
class BasicPlot : public QCustomPlot {
    CREEPER_PIMPL_DEFINITION(BasicPlot)
    friend class HeatMapPlot;
public:
    // BasicPlot();
    // ~BasicPlot();
    // BasicPlot(const BasicPlot&)             = delete;
    // BasicPlot& operator=(const BasicPlot&) = delete;

    void init_plot()const;
    void load_theme_manager(ThemeManager&)const;
    void set_xlabel_text(const QString&)const;
    void set_ylabel_text(const QString&)const;
    void set_matrix_size(const QSize&)const;
    void set_matrix_size(const int& w, const int& h)const;
    void set_data(const QVector<PointData>& data)const;
    void set_color_gradient_range(const double& min, const double& max)const;
    QSize get_matrix_size() const;
    bool is_initialized() const;

public slots:
    void update_dynamic_heatmap(const QVector<PointData>& map)const;
    void dataChanged(const QVector<PointData>& map)const;
    void dataRangeChanged(const double& min, const double& max)const;

protected:
    void paintEvent(QPaintEvent*) override;

private:
    friend struct Impl;

};
} // namespace plot_widget::internal

namespace plot_widget::pro {
    using Token = common::Token<plot_widget::internal::BasicPlot>;
    
    using XLabelText = common::pro::String<Token, 
        [](auto& self, const auto& string) {
            self.set_xlabel_text(string);
        }>;
    
    using YLabelText = common::pro::String<Token, 
        [](auto& self, const auto& string) {
            self.set_ylabel_text(string);
        }>;

    struct MatrixSize : Token {
        QSize size;
        explicit MatrixSize(const int& w, const int& h) : size{w, h} {}
        explicit MatrixSize(const QSize& s) : size{s} {}
        void apply(auto& self) const {
            self.set_matrix_size(size);
        }
    };

    using Data = common::pro::Vector<Token, PointData, 
        [](auto& self, const auto& data) {
            self.set_data(data);
        }>;
    
    struct ColorRange : Token {
        double min;
        double max;
        explicit ColorRange(double min, double max) : min{min}, max{max} {}
        void apply(auto& self) const {
            self.set_color_gradient_range(min, max);
        }
    };

    template <typename F>
    using OnDataChanged = 
        common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataChanged>;
    
    template <typename F>
    using OnDataRangeChanged =
        common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataRangeChanged>;

    template<class PlotWidget>
    concept trait = std::derived_from<PlotWidget, Token>;

    using PlotData = common::pro::Vector<Token, PointData, [](auto& self, const auto& vec) {
        self.set_data(vec);
    }>;

    using DataRange = common::pro::Array<Token, int, 2, [](auto& self, const auto& arr) {
        self.set_color_gradient_range(arr[0], arr[1]);
    }>;

    CREEPER_DEFINE_CHECKER(trait);
    using namespace widget::pro;
    using namespace theme::pro;
}

struct HeatMapPlot
    : public Declarative<plot_widget::internal::BasicPlot,
        CheckerOr<plot_widget::pro::checker, widget::pro::checker, theme::pro::checker>> {
    using Declarative::Declarative;
    void paintEvent(QPaintEvent*) override;
};
}

#endif // TOUCHSENSOR_HEATMAP_H
