#pragma once

#include "Density.h"
#include "Polygoniser.h"
#include "TextureMapper.h"
#include "ProgressMonitor.h"
#include "SkyBox.h"
#include "gl.h"

#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>

#include <string>
#include <thread>

struct GLFWwindow;

class Application {
	GLFWwindow* window;
	int width, height;

	Density density;
	float densityIsolevel;
	Polygonizer polygonizer;
	float textureSize;
	TextureMapper texturemapper;

	int vPositionLoc, vTexCoordLoc, vNormalLoc;
	unsigned int programId, tAlbedoId, tNormalId, tRoughnessId, tMetalicId;

	int MVPloc, Vloc, Mloc, MVloc, LightPosition_worldspaceloc;
	int tAlbedoLoc, tNormalLoc, tRoughnessLoc, tMetalicLoc;

	Mesh mesh;
	GLMesh glMesh;

	size_t meteoridCount;
	float meteroidMinRadius;
	float meteroidMaxRadius;
	int meteroidSizeExponent;
	float densityFrequency, densityOctave;
	glm::vec3 densityScale;
	float resolution;
	bool smoothMesh;

	float viewDistance;

	glm::mat4 fpsTrafo;
	bool fpsMode;
	double lastMouseX, lastMouseY;
	glm::mat4 modelTrafo;

	ProgressMonitor meshProgress, textureProgress;
	std::thread meshThread, textureThread;
	bool meshUploadRequest, textureUploadRequest;

	void toogleFpsMode();

	glm::mat4 perspective, view, mvp;

	SkyBox skybox;

public:
	Application();

	~Application();

	static Application &instance();

	void init();

	void shutdown();

	void KeyCallback(GLFWwindow* window, int key, int scancode, int action,
			int mods);
	void CursorCallback(GLFWwindow* window, double x, double y);
	void MouseButtonCallback(GLFWwindow* window, int button, int action,
			int mods);
	void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
	void CharCallback(GLFWwindow* window, unsigned int c);

	void generateAsteroid();

	bool bindVertexAttrib(unsigned int id, int loc, unsigned int size);

	void updateVertexArrayObject();

	void loadShader();

	void uploadMesh();
	void uploadTexture();

	void renderMesh();

	void updateMatrices();
	void prepareShader();

	void gui();

	void run();
};
