#ifndef MAPVIEW_H
#define MAPVIEW_H

#include <../misc/fixc11.h>


#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QBasicTimer>

#include <Indoor/geo/Point3.h>
#include <Indoor/nav/dijkstra/DijkstraPath.h>

#include "elements/Path.h"
#include "elements/ColorPoints.h"
#include "elements/Object.h"

#include "../nav/State.h"

namespace Floorplan {
	class IndoorMap;
}

class Renderable;
class Path;


class MapView3D : public QOpenGLWidget, protected QOpenGLFunctions {

	Q_OBJECT

private:

	QMatrix4x4 matProject;
	QMatrix4x4 matView;

	QVector3D lightPos;
	QVector3D eyePos;

	RenderParams renderParams;

	QBasicTimer timer;

	std::vector<Renderable*> elements;
	Path* pathToDest = nullptr;
	Path* pathWalked = nullptr;
	ColorPoints* colorPoints = nullptr;
	Object* leDude = nullptr;

	struct LookAt {
		QVector3D eye_m = QVector3D(0,0,1);
		QVector3D dir = QVector3D(1,0,-0.1);
		float ax = 0;
		float ay = 0;
		QMatrix4x4 dirRot;
		QVector3D getDir() const {return this->dirRot * this->dir;}
		LookAt() {
			dirRot.setToIdentity();
		}
	} lookAt;

	struct MouseState {
		float x = 0;
		float y = 0;
		bool down = false;
	} mouseState;

	void rebuildLookat();

	void clear();


public:

	MapView3D(QWidget* parent = 0);

	/** set the map to display */
	void setMap(Floorplan::IndoorMap* map);

	/** the position to look at + looking direction */
	void setLookAt(const Point3 pos, const Point3 dir = Point3(1, 0, -0.1));

	/** set the eye's looking direction (looking from eye into this direction) */
	void setLookDir(const Point3 dir);

	/** set the eye's position (looking from here) */
	void setLookEye(const Point3 eye_m);

	/** do not render elements higher than the given height */
	void setClipAbove(const float height_m) {renderParams.clipAboveHeight_m = height_m;}


	/** set the currently estimated position */
	void setCurrentEstimation(const Point3 pos, const Point3 dir);


	/** set the path to the destination MUST BE CALLED FROM THE MAIN THREAD */
	void setPathToDestination(const std::vector<Point3>& path);

	/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD */
	template <typename Node> void setPathToDestination(const DijkstraPath<Node>* path) {this->pathToDest->set(*path);}

	/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD*/
	Q_INVOKABLE void setPathToDestination(const void* path) { setPathToDestination( (const DijkstraPath<MyGridNode>*) path); }


	/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
	void setPathWalked(const std::vector<Point3>& path);

	/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
	Q_INVOKABLE void setPathWalked(const void* path) { this->pathWalked->set( *((const std::vector<Point3>*) path)); }





	/** NOTE: must be called from Qt's main thread! */
	void showGridImportance(Grid<MyGridNode>* grid) {
		this->colorPoints->setFromGridImportance(grid);
	}

	/** NOTE: must be called from Qt's main thread! */
	Q_INVOKABLE void showParticles(const void* particles) {
		showParticles((const std::vector<K::Particle<MyState>>*) particles);
	}

	/** NOTE: must be called from Qt's main thread! */
	void showParticles(const std::vector<K::Particle<MyState>>* particles) {
		this->colorPoints->setFromParticles(*particles);
	}

	enum RenderMode {
		NORMAL,
		TRANSPARENT,
		OUTLINE,
	};

	RenderMode renderMode = RenderMode::NORMAL;

	void toggleRenderMode();

	void setVisible(bool visible) override;

public slots:

	void mousePressEvent(QMouseEvent*);
	void mouseMoveEvent(QMouseEvent*);
	void mouseReleaseEvent(QMouseEvent*);

	void keyPressEvent(QKeyEvent*);

protected:

	void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE;

	void initializeGL();

	void paintGL();

	void resizeGL(int width, int height);

private:

	bool isGLInitialized = false;

	void draw();

};

#endif // MAPVIEW_H
