#pragma once

//////////////
// Includes //
//////////////
// C++ general includes
#include<string>
#include<iostream>

// OpenGL Includes
#include <glad/glad.h>
#include <GLFW/glfw3.h> // GLFW must be included after GLAD!
#include "include/stb_image.h"
#include "include/rtrSetupGLFWandGLAD.h" //rtrGLFWGLAD::
#include "include/rtrShader.h" //rtrShader::
#include "include/rtrVAO.h"
#include "include/rtrCamera.h"
#include "include/rtrModelLoading.h"
#include "include/rtrMaterialsAndLighting.h"
#include "include/rtrCubeMap.h"
#include "include/newYearsEve.h"
#include "include/rtrCamera.h"
#include "include/rtrShadows.h"

#include "include/fps.h"

////////////////
// Parameters //
////////////////

#define NUMBER_OF_LIGHT_SOURCES 4

//////////////
// Main     //
//////////////

std::vector<DrawableObject*> objects_to_draw;

int main()
{
	// Define identity matrix
	const glm::mat4 idMat = glm::mat4(1.0f);

	// Set up a rendering window using GLFW and GLAD
	int retVal;
	glm::vec2 resolution(1920, 1080); // Must be 1920, 1080
	GLFWwindow* window = rtrGLFWGLAD::setupWindow(retVal, (int)resolution.x, (int)resolution.y,"New Year's Eve", true);

	// Render Start Screen 
	fireworks::renderStartScreen(window, resolution);

	//Setup FrameCounter
	fps::FrameCounter frameCounter(resolution);

	// Setup Camera
	rtrCamera::CameraForGLFW camera(window);
	camera.setFOVCanBeChanged(false);
	camera.Position.z = -0; // step back far enough so we can see the cube
	camera.Position.y = 2; // Camera on eye height
	camera.isAutomatic = true;

	// Create Sky Box Object (brings it sown shader, parameters etc.)
	CubeMap skyBox("textures\\cubeMapGrimmNight\\");
	skyBox.strength = 0.8;
	// Create Rocket
	fireworks::Rocket rocket;

	//Setup PointShadow thrown by lightCube
	rtrShadows::PointShadow pointShadow(&(rocket.Position));
	pointShadow.debug = false;

	// City Model: Load with AssImp and configure its Shader
	rtrModelLoading::PhysicalObject cityModel("models\\old_town_block\\old_town_block.obj", "shaders\\model.vert", "shaders\\model.frag", pointShadow.ShadowCubeMap);
	fireworks::configureCityModel(cityModel);
	pointShadow.sendDataToShader(cityModel.Shader);

	//Setup Array of lightSources
	rtrMaterialsAndLighting::Light lightSources[NUMBER_OF_LIGHT_SOURCES];
	fireworks::configureLights(lightSources, rocket, cityModel, camera);

	//Setup iventory displaying a lighter
	fireworks::Inventory inventory(resolution);
	
	//Setup a script for camera movement
	fireworks::CameraScript cameraScript(window, &rocket, &camera, &inventory);

	// add all models to the DRAWING vector
	objects_to_draw.push_back(&skyBox);
	objects_to_draw.push_back(&cityModel);
	objects_to_draw.push_back(&rocket);
	objects_to_draw.push_back(&inventory);
  
	// reset time counter
	glfwSetTime(0);

	////////////////////
	// Rendering Loop //
	////////////////////
	while (!glfwWindowShouldClose(window))
	{
		////////////////
		// Loop Setup //
		////////////////
		// Process mouse and keyboard input for camera control (Position and Orienation of camera) or control camera according to script
		camera.checkIfUserSwitchedtoManulaMode(window);
		if (camera.isAutomatic) // Automatic camera always updates camera
		{
			cameraScript.Apply();
			camera.updateCameraAtNewFrame();
		}
		else if (!inventory.isOpen) // manual camera: only allow to move when inventory not open
		{
			camera.updateCameraAtNewFrame();
		}

		// tells glfw to close Window if escape key was pressed
		rtrGLFWGLAD::checkIfWindowShouldClose(window);

		// Setup OPenGL Parameters
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_DEPTH_TEST);

		// Update Framerate data every second
		frameCounter.update();


		//////////////////////////////////////
		// Update time dependent Parameters //
		//////////////////////////////////////
		// Save current View and Projection Matrices
		glm::mat4 viewMatrix = camera.GetViewMatrix();
		glm::mat4 projectionMatrix = camera.GetProjectionMatrix();

		// Update rocket Position and color intensity
		rocket.UpdateParameters(frameCounter.passedTimeSinceLastFrame);
		
		// Update Parameters of moving light sources and send to shader (regards Point light in Light Cube and Flash light)
		fireworks::updateLights(window, camera, NUMBER_OF_LIGHT_SOURCES, lightSources, cityModel, rocket);

		// Check if iventory should be displayed and if lighter was used
		inventory.update(camera.Position, rocket.Position, rocket.Status);
		if (inventory.lighterSelected) rocket.Status = USINGLIGHTER;

		// Check if shadows where toggeld and create point shadow depth map from firework light source
		pointShadow.processKeyboardInput(window);
		pointShadow.sendDataToShader(cityModel.Shader);
		pointShadow.renderDepthMap(cityModel);
		glViewport(0, 0, resolution.x, resolution.y); // reset after Viewport was chaged in last line

		// Send updated data to shaders ( inlcuding view & projection matrices and camera position)
		rocket.UpdateShader(viewMatrix, projectionMatrix);
		cityModel.UpdateShader(viewMatrix, projectionMatrix, camera.Position);
		skyBox.UpdateViewAndProjectionMatrices(viewMatrix, projectionMatrix);
		

		//////////
		// Draw //
		//////////
		for (int i = 0; i < objects_to_draw.size(); i++)
			objects_to_draw[i]->Draw();
		pointShadow.drawDepthScreen(rocket.Position); // Happens only in if debug option active
    

		// Swap front and back buffer (one for drawing one for displaying)
		glfwSwapBuffers(window);

		// Check for events (like Keyboard input)
		glfwPollEvents();
	}


	// clean up all resources that were allocated to glfw and exit
	glfwTerminate();
	return 0;
}










