#ifndef GROUND_H
#define GROUND_H

#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../gl/GLLines.h"
#include "../Renderable.h"

#include "../../../../lib/gpc/Polygon.h"

class Ground : public Renderable {

private:

	Floorplan::Floor* floor;

	GLTriangles<VertNormTexTan> flooring;
	GLTriangles<VertNormTexTan> ceiling;

	GLLines outline;

	bool outlineOnly = false;
	bool transparent = false;

public:

	/** ctor */
	Ground(Floorplan::Floor* floor) : floor(floor) {	
		setOutlineOnly(false);
	}


	void initGL() override {

		build();

		flooring.setDiffuse(":/res/gl/tex/floor4.jpg");
		flooring.setNormalMap(":/res/gl/tex/floor4_normal.jpg");

		ceiling.setDiffuse(":/res/gl/tex/floor4.jpg");
		ceiling.setNormalMap(":/res/gl/tex/floor4_normal.jpg");

		flooring.build();
		ceiling.build();
		outline.build();

		//loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
		//program.setUniformValue("texDiffuse", 0);
		//program.setUniformValue("texNormalMap", 1);

	}

	/** render the floor */
	void _render(const RenderParams& params) override {

		// skip me?
		if (params.clipAboveHeight_m < floor->atHeight) {return;}

		if (transparent) {
			program.setUniformValue("alpha", 0.5f);
			glEnable(GL_BLEND);
		} else {
			program.setUniformValue("alpha", 1.0f);
			glDisable(GL_BLEND);
		}

		if (outlineOnly) {
			glLineWidth(5);
			outline.render(&program);
		} else {
			flooring.render(&program);
			ceiling.render(&program);
		}

		glDisable(GL_BLEND);

	}

	/** render only the outline? */
	void setOutlineOnly(const bool outline) override {
		(void) outline;
//		this->outlineOnly = outline;
//		if (outlineOnly) {
//			loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
//			program.setUniformValue("color", QVector4D(0.0, 0.0, 0.4, 1.0));
//		} else {
			loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
			program.setUniformValue("texDiffuse", 0);
			program.setUniformValue("texNormalMap", 1);
//		}
	}

	void setTransparent(const bool transparent) override {
		this->transparent = transparent;
	}



private:

	void build() {

		std::vector<gpc_polygon> add;
		std::vector<gpc_polygon> rem;

		const std::vector<Floorplan::FloorOutlinePolygon*>& polys = floor->outline;

		Polygon pol;

		for (Floorplan::FloorOutlinePolygon* poly : polys) {
			switch (poly->method) {
			case Floorplan::OutlineMethod::ADD:		pol.add(poly->poly);	break;
			case Floorplan::OutlineMethod::REMOVE:	pol.remove(poly->poly);	break;
			default:								throw 1;
			}
		}

		std::vector<std::vector<Point3>> trias = pol.get(floor->atHeight);


		// is stored as TRIANGLE_STRIP
		// => triangle might have more than 3 points
		for (const std::vector<Point3>& tria : trias) {


			const QVector3D normFloor(0, +1, 0);
			const QVector3D normCeil(0, -1, 0);
			const QVector3D t(1,0,0);

			const float s = 0.6;

			// add vertices
			for (int i = 2; i < (int) tria.size(); i+=1) {

				const Point3 p1 = tria[i-2];
				const Point3 p2 = tria[i-1];
				const Point3 p3 = tria[i-0];

				const QVector3D vert1(p1.x, p1.z, p1.y);
				const QVector3D vert2(p2.x, p2.z, p2.y);
				const QVector3D vert3(p3.x, p3.z, p3.y);

				{
					const VertNormTexTan vnt1(vert1, normFloor, tex(vert1*s), t);
					const VertNormTexTan vnt2(vert2, normFloor, tex(vert2*s), t);
					const VertNormTexTan vnt3(vert3, normFloor, tex(vert3*s), t);
					flooring.addFaceCCW(vnt1, vnt2, vnt3);
				} {
					const VertNormTexTan vnt1(vert1, normCeil, tex(vert1*s), t);
					const VertNormTexTan vnt2(vert2, normCeil, tex(vert2*s), t);
					const VertNormTexTan vnt3(vert3, normCeil, tex(vert3*s), t);
					ceiling.addFaceCW(vnt1, vnt2, vnt3);
				}

				outline.addLine(vert1, vert2);
				outline.addLine(vert2, vert3);
				outline.addLine(vert3, vert1);

			}

		}

	}


private:

	QVector2D tex(const QVector3D vert) {
		return QVector2D(vert.x(), vert.z());
	}




};

#endif // GROUND_H
