#ifndef PAPERPLOT_H
#define PAPERPLOT_H

#include <KLib/misc/gnuplot/Gnuplot.h>

#include <KLib/misc/gnuplot/GnuplotSplot.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>


#include <Indoor/floorplan/Floor.h>
#include <Indoor/geo/Length.h>

class PaperPlot {

public:

	struct Size {
		float w;
		float h;
		Size(const float w, const float h) : w(w), h(h) {;}
	};


public:

	std::string file;
	K::Gnuplot gp;
	K::GnuplotSplot plot;

	K::GnuplotSplotElementLines floors;
	K::GnuplotSplotElementColorPoints nodes;
	K::GnuplotSplotElementLines edgesSame;
	K::GnuplotSplotElementLines edgesStair;

public:

	PaperPlot(const std::string& file, Size s) : file(file) {
		toFile(file, s);
		setup();
	}

	PaperPlot() {
		setup();
	}

	void toFile(const std::string& file, const Size s) {
		gp << "set output '" << file << "'\n";
		gp << "set terminal eps size " << s.w << "," << s.h << "\n";
	}

	void setup() {

		floors.setLineWidth(2);

		plot.add(&edgesSame);
		plot.add(&edgesStair);

		plot.add(&nodes);
		plot.add(&floors);

		nodes.setPointSize(0.2);

		edgesSame.setColorHex("#555555");
		edgesStair.setColorHex("#AAAA55");


		gp << "set ticslevel 0\n";

	}

	void show() {
		gp.draw(plot);
		if (file.length() != 0) {
			std::string dataFile = file + ".dat";
			std::ofstream os(dataFile.c_str());
			os << gp.getBuffer();
			os.close();
		}
		gp.flush();;
	}

	/** add all obstacles of the given floor to the provided height */
	void addFloor(const Floor& f, const LengthF height) {

		// add each wall
		for (const Line2& l : f.getObstacles()) {
			const K::GnuplotPoint3 p1(l.p1.x, l.p1.y, height.cm());
			const K::GnuplotPoint3 p2(l.p2.x, l.p2.y, height.cm());
			floors.addSegment(p1, p2);
		}

	}

	/** add the grid to the plot */
	template <typename T> void addGrid(Grid<T>& grid) {

//		std::set<uint64_t> used;

		// get the min/max value
		float max = -999999;
		float min = +999999;
		for (const T& n1 : grid) {
			const float val = n1.imp;
			//const float val = n1.distToTarget;
			if (val > max) {max = val;}
			if (val < min) {min = val;}
		}
		gp << "set cbrange["<<min<<":"<<max<<"]\n";

		for (const T& n1 : grid) {
			const K::GnuplotPoint3 p1(n1.x_cm, n1.y_cm, n1.z_cm);
			const float color = n1.imp;
			//const float color = n1.distToTarget/max;
			//const float color = 0;
			nodes.add(p1, color);
//			for (const T& n2 : grid.neighbors(n1)) {
//				const uint64_t idx = n1.getIdx() * n2.getIdx();
//				if (used.find(idx) == used.end()) {
//					const K::GnuplotPoint3 p2(n2.x_cm, n2.y_cm, n2.z_cm);
//					gridEdges.addSegment(p1, p2);
//					used.insert(idx);
//				}
//			}
		}

	}

	/** show all nodes (and edges?) within the given region */
	template <typename T> void debugGrid(Grid<T>& grid, const BBox3& bbox, const bool addNodes, const bool addEdges, const bool addAllEdges = false) {

		std::set<uint64_t> used;

		for (T& n1 : grid) {
			if (bbox.contains(n1)) {
				const K::GnuplotPoint3 p1(n1.x_cm, n1.y_cm, n1.z_cm);
				if (addNodes) {
					nodes.add(p1, 0);
				}
				if (addEdges) {
					for (const T& n2 : grid.neighbors(n1)) {
						if (n1.z_cm == n2.z_cm && !addAllEdges) {continue;}	// speedup
						if (used.find(n2.getIdx()) == used.end()) {
							const K::GnuplotPoint3 p2(n2.x_cm, n2.y_cm, n2.z_cm);
							if		(p1.z != p2.z)	{edgesStair.addSegment(p1, p2);}
							else					{edgesSame.addSegment(p1, p2);}
						}
					}
					used.insert(n1.getIdx());
//					for (const T& n2 : grid.neighbors(n1)) {
//						const uint64_t idx = n1.getIdx() * n2.getIdx();
//						if (used.find(idx) == used.end()) {
//							const K::GnuplotPoint3 p2(n2.x_cm, n2.y_cm, n2.z_cm);
//							edges.addSegment(p1, p2);
//							used.insert(idx);
//						}
//					}
				}
			}
		}

	}


};

#endif // PAPERPLOT_H
