//
// Created by mark on 5/30/25.
//

#ifndef CHARTJSLIBRARY_H
#define CHARTJSLIBRARY_H

#include <Wt/WAbstractItemModel.h>
#include <Wt/WContainerWidget.h>
#include <Wt/Chart/WAbstractChartModel.h>
#include <Wt/Json/Array.h>
#include <Wt/Json/Object.h>
#include <Wt/WSignal.h>

enum class ChartJSType
	{
		BAR, BUBBLE, DOUGHNUT, PIE, LINE, POLAR, RADAR, SCATTER
	};

	enum class Location
	{
		TOP, BOTTOM, LEFT, RIGHT
	};

	enum class HeaderText {
		HORIZONTAL = 'H', VERTICAL = 'V'
	};
	// typedef std::map<std::string, cpp17::any> SettingsMapType;

	// Defaults set. Override if necessary
	// chart colors are as follows
	// red: 'rgb(255, 99, 132)',
	// orange: 'rgb(255, 159, 64)',
	// yellow: 'rgb(255, 205, 86)',
	// green: 'rgb(75, 192, 192)',
	// blue: 'rgb(54, 162, 235)',
	// purple: 'rgb(153, 102, 255)',
	// grey: 'rgb(201, 203, 207)'


	// This class will always take on the same size as the parent container
	class WChartJSLibrary : public Wt::WContainerWidget
	{
	public:
		WChartJSLibrary(ChartJSType);
		~WChartJSLibrary() override;

		Wt::JSignal<int, int>& chartClicked() { return chartClicked_; }
		// Returns the WStandardItemModel indexes
		[[nodiscard]] std::pair<int, int> getModelCoordinates(int datasetIndex, int dataIndex) const;

		// When calling this object, all desired options must be populated and
		// completed with a call to showChart(). You cannot delay showing the chart if
		// it's container is visible and this may show a malformed chart if you neglect
		// this call at the end of initializing the data and options.
		void buildAndShowChart();

		// NOTE: This object tries to be as similar in operation to the Wt charts as
		// possible in terms of the WStandardItemModel and Wt::Signals to change or update
		// data through the Wt engines. Also incorporated is the WText/WString methods
		// in order to maintain consistency with Wt translations. WSeries is too unique to
		// Wt's javascript charting, so only the Model column is kept and serves as an index
		// to series specific options.

		std::string getChartJSType(ChartJSType);
		void setModel(const std::shared_ptr<Wt::WAbstractItemModel>& model);
		void setModel(const std::shared_ptr<Wt::Chart::WAbstractChartModel>& model);
		std::shared_ptr<Wt::WAbstractItemModel> itemModel() const;
		void addSeries(int modelColumn, bool countForLabels, Wt::Json::Object dataSeriesOptions, bool dependantIndependantSeries);
		void addRawSeries(const Wt::Json::Object& rawSeries);
		void resetSeries(int modelColumn);
		void removeSeries(int modelColumn);
		void removeLastRawSeries();
		void updateExistingSeries();
		void setXaxisLabels(const std::vector<std::string>&);
		void updateChartTitleOnly(const Wt::WString& title);
		void setOptions(const Wt::Json::Object& chartOptions);

	protected:
		void layoutSizeChanged(int width, int height) override;
		void render(Wt::WFlags<Wt::RenderFlag> flags) override;
		void modelChanged();
		void updateModel();

	private:
		void defineJavaScript();
		Wt::JSignal<int, int> chartClicked_;
		Wt::Json::Object chartjsConfigObj{}; // This is where everything is defined
		ChartJSType chartType_{};
		std::string chartTypeName_{};
		Wt::Json::Object chartOptions_{};
		Wt::Json::Object plugins_{};
		Wt::Json::Object chartData_{};
		Wt::Json::Object netDataChangeJson_{}; // after chart creation to add net change
		std::shared_ptr<Wt::Chart::WAbstractChartModel> model_ = nullptr;
		std::shared_ptr<Wt::WAbstractItemModel> standardModel_ = nullptr;
		// connections with the current model, used to disconnect from a model
		// when the model changes.
		std::vector<Wt::Signals::connection> modelConnections_{};

		struct ModelColumnInfo
		{
			int modelColumnNumber{};
			bool countForLabels{};
			bool dependantIndependantSeries{};
			int lastDataCount{};
		};

		std::vector<ModelColumnInfo> modelColumnsLoaded_{};
		Wt::Json::Array series_{};
		Wt::Json::Array dataLabels_{};
		bool resized_ = false;
		bool legendEnabled_ = false;
		std::string legendLocation_{};
		Wt::WStringStream legendOptions_{};
		bool countForLabels_ = false;
		bool titleEnabled_ = false;
		Wt::WString titleText_{};
		bool fullyRendered_ = false;
		bool jsDefined_{};

		template <typename T>
		void set(T& m, const T& v);
	};

	template <typename T>
	void WChartJSLibrary::set(T& m, const T& v) {
		if (m != v) {
			m = v;
		}
	}


#endif //CHARTJSLIBRARY_H
