#ifndef PLOT_H
#define PLOT_H

#include <QPainter>

#include "Axes.h"
#include "Data.h"

#include <Indoor/geo/Point2.h>

/** describes a plot-setup */
struct PlotParameters {

	AxesX xAxes;
	AxesY yAxes;

	int w;
	int h;

	/** helper method */
	Point2 getPoint(const typename Data::KeyVal& kv) const {
		const float x1 = xAxes.convert(kv.key);
		const float y1 = yAxes.convert(kv.val);
		return Point2(x1, y1);
	}

};

/** interface for all plots */
class Plot {

protected:

	Data data;

	QColor bg = QColor(255,255,255,128);

public:

	virtual ~Plot() {;}

	void render(QPainter& p, const PlotParameters& params) {
		// maybe do something here?
		renderSub(p, params);
	}

	Data& getData() {return data;}

	Range getKeyRange() const {return data.getKeyRange();}

	Range getValueRange() const {return data.getValueRange();}

protected:

	/** subclasses must render themselves here */
	virtual void renderSub(QPainter& p, const PlotParameters& params) = 0;

};



/** combine several plots (several lines, points, ...) together into one plot */
class PlotContainer {

private:

	PlotParameters params;

	std::vector<Plot*> plots;

public:

	/** ctor */
	PlotContainer() {
		;
	}

	AxesX& getAxesX() {return params.xAxes;}
	AxesY& getAxesY() {return params.yAxes;}

	Range overwriteRangeKey = Range(0,0);
	Range overwriteRangeVal = Range(0,0);

	void setValRange(const Range& range) {overwriteRangeVal = range;}
	void setKeyRange(const Range& range) {overwriteRangeKey = range;}


	void resize(const int w, const int h) {
		params.w = w;
		params.h = h;
	}

	void addPlot(Plot* plt) {
		plots.push_back(plt);
	}

	void render(QPainter& p) {

		setupAxes();

		QColor bg(255,255,255,192);
		p.fillRect(0,0,params.w,params.h,bg);
		p.setPen(Qt::black);
		p.drawRect(0,0,params.w-1,params.h-1);

		for (Plot* plt : plots) {
			plt->render(p, params);
		}

	}



	void setupAxes() {

		params.xAxes.setPixels(params.w);
		params.yAxes.setPixels(params.h);

		Range keyRange(+INFINITY,-INFINITY);
		Range valRange(+INFINITY,-INFINITY);

		if (overwriteRangeKey.isValid()) {keyRange.adjust(overwriteRangeKey);}
		if (overwriteRangeVal.isValid()) {valRange.adjust(overwriteRangeVal);}

		/** calculate min/max for both x and y axis */
		for (Plot* plt : plots) {
			if (!overwriteRangeKey.isValid()) {keyRange.adjust(plt->getKeyRange());}
			if (!overwriteRangeVal.isValid()) {valRange.adjust(plt->getValueRange());}
		}

		valRange.scale(1.1);

		params.xAxes.setRange(keyRange);
		params.yAxes.setRange(valRange);

	}

};



class LinePlot : public Plot {

private:

	QColor lineColor = QColor(0,0,255);

public:

	void setColor(const QColor c) {
		this->lineColor = c;
	}

protected:

	void renderSub(QPainter& p , const PlotParameters& params) override {

		p.setPen(lineColor);

		for (int i = 0; i < (int) data.size()-1; ++i) {

			const typename Data::KeyVal kv1 = data[i+0];
			const typename Data::KeyVal kv2 = data[i+1];

			const Point2 p1 = params.getPoint(kv1);
			const Point2 p2 = params.getPoint(kv2);

			p.drawLine(p1.x, p1.y, p2.x, p2.y);

		}

	}

};


class PointPlot : public Plot {

private:

	QColor pointColor = QColor(0,0,255);
	float pointSize = 4;

public:

	void setColor(const QColor c) {
		this->pointColor = c;
	}

	void setPointSize(const float size) {
		this->pointSize = size;
	}

protected:

	void renderSub(QPainter& p , const PlotParameters& params) override {

		p.setPen(Qt::NoPen);
		p.setBrush(pointColor);

		for (int i = 0; i < (int) data.size(); ++i) {

			const typename Data::KeyVal kv1 = data[i+0];

			const Point2 p1 = params.getPoint(kv1);

			p.drawEllipse(p1.x, p1.y, pointSize, pointSize);

		}

	}

};

#endif // PLOT_H
