// Std. Includes
#include <string>

// GLEW
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// GL includes
#include "Shader.h"
#include "Camera.h"
#include "Model.h"

// GLM Mathemtics
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>

// Other Libs
#include <SOIL.h>

// Properties
GLuint screenWidth = 1600, screenHeight = 900;

// Function prototypes
void calcLight(Shader shader);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void Do_Movement();

GLuint loadCubemap(vector<const GLchar*> faces);


// Camera
Camera camera(glm::vec3(1, 0.5, -0.6));
bool keys[1024];
GLfloat lastX = 800, lastY = 450;
bool firstMouse = true;

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

// Positions of the point lights
glm::vec3 pointLightPositions[] = {
	glm::vec3(6, 10, -50),
	glm::vec3(7, 22, -134),
	glm::vec3(24, 13, -52),
	glm::vec3(25, 30, -213),
	glm::vec3(-80, 19, -190),
	glm::vec3(-24, 51, -530),
	glm::vec3(255, 39, -177),
	glm::vec3(177, 47, -156),
	glm::vec3(177, 47, -234),
	glm::vec3(121, 35, -179)
};

#include "cubemap.h"

// The MAIN function, from here we start our application and run our Game loop
int main()
{
	// Init GLFW
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

	GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
	glfwMakeContextCurrent(window);

	// Set the required callback functions
	glfwSetKeyCallback(window, key_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// Options
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// Initialize GLEW to setup the OpenGL Function pointers
	glewExperimental = GL_TRUE;
	glewInit();

	// Define the viewport dimensions
	glViewport(0, 0, screenWidth, screenHeight);

	// Enable depth test
	glEnable(GL_DEPTH_TEST);
	// Accept fragment if it closer to the camera than the former one
	glDepthFunc(GL_LESS);

	// Setup and compile our shaders
	Shader shader("normalvertex.vertex", "normalfragment.fragment");
	Shader skyboxShader("skybox.vertexshader", "skybox.fragmentshader");

	
	// Setup skybox VAO
	GLuint skyboxVAO, skyboxVBO;
	glGenVertexArrays(1, &skyboxVAO);
	glGenBuffers(1, &skyboxVBO);
	glBindVertexArray(skyboxVAO);
	glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
	glBindVertexArray(0);

	vector<const GLchar*> faces;
	faces.push_back("Models/World/Test/skybox4/front.png");
	faces.push_back("Models/World/Test/skybox4/back.png");
	faces.push_back("Models/World/Test/skybox4/top.png");
	faces.push_back("Models/World/Test/skybox4/bottom.png");

	faces.push_back("Models/World/Test/skybox4/left.png");

	faces.push_back("Models/World/Test/skybox4/right.png");
	GLuint cubemapTexture = loadCubemap(faces);


	// Load models
	Model level("Models/World/Test/completeLevel.obj");
	Model improve("Models/World/Test/improvement.obj");
	Model pyramid("Models/World/Test/pyramid.obj");
	Model reflect("Models/World/Test/reflect.obj");

	Model Firstswitch("Models/World/Test/switch.obj");
	Model FirstDoor("Models/World/Test/door.obj");

	Model Secoundswitch("Models/World/Test/switch2.obj");
	Model SecoundDoor("Models/World/Test/door2.obj");

	Model EndDoor("Models/World/Test/door3.obj");
	Model MiddleDoor("Models/World/Test/door4.obj");
	Model FrontDoor("Models/World/Test/door5.obj");
	Model EntranceDoor("Models/World/Test/door6.obj");

	Model head("Models/World/Test/head.obj");
	Model body("Models/World/Test/body.obj");
	Model hand("Models/World/Test/hand.obj"); 

	Model frontkinfe("Models/World/Test/frontknife.obj");
	Model middlekinfe("Models/World/Test/middleknife.obj");
	Model lastkinfe("Models/World/Test/lastknife.obj");
	
	Model kinfe1("Models/World/Test/knife1.obj");
	Model kinfe2("Models/World/Test/knife2.obj");
	Model kinfe3("Models/World/Test/knife3.obj");
	Model kinfe4("Models/World/Test/knife4.obj");

	Model key1("Models/World/Test/key.obj");
	Model key2("Models/World/Test/key2.obj");
	Model key3("Models/World/Test/key3.obj");

	Model KeyinHole1("Models/World/Test/KeyinHole1.obj");
	Model KeyinHole2("Models/World/Test/KeyinHole2.obj");
	Model KeyinHole3("Models/World/Test/KeyinHole3.obj");


	// Draw in wireframe
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glm::mat4 modelSwitch1;
	glm::mat4 modelDoor1;
	float moveDoor1 = 11;

	glm::mat4 modelSwitch2;
	glm::mat4 modelDoor2;
	float moveDoor2 = -6.55;

	glm::mat4 modelDoor3;
	float moveDoor3 = 51.7321;

	glm::mat4 modelMiddleDoor;
	float moveDoor4 = 51.7321;

	glm::mat4 modelFrontDoor;
	float moveDoor5 = 51.7321;

	glm::mat4 modelEntranceDoor;
	float moveDoor6 = 21.71366;
	
	glm::mat4 modelHead;
	glm::mat4 modelBody;
	glm::mat4 modelHand;

	glm::mat4 modelFrontKnife;
	glm::mat4 modelMiddleKnife;
	glm::mat4 modelLastKnife;

	
	bool pickedUp1 = false;
	bool pickedUp2 = false;
	bool pickedUp3 = false;
	bool used1 = false;
	bool used2 = false;
	bool used3 = false;
	

	glm::mat4 modelKnife1;
	glm::mat4 modelKnife2;
	glm::mat4 modelKnife3;
	glm::mat4 modelKnife4;
	float moveknife1 = 45;
	float moveknife2 = 70;
	float moveknife3 = 52;
	float moveknife4 = 73;
	bool NegativeX1 = true;
	bool NegativeX2 = true;
	bool NegativeX3 = true;
	bool NegativeX4 = true;

	///****MAGDY
	bool death = true;
	///****MAGDY


	glm::vec3 translation ;
	glm::vec3 rotation;
	float rotationangle;

	float move = 28;
	bool up = true;
	bool orbit = false;

	bool spinFrontKnife = true;
	bool spinMiddleKnife = true;
	bool spinLastKnife = true;
	// Game loop
	while (!glfwWindowShouldClose(window))
	{
		// Set frame time
		GLfloat currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		// Clear the screen
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Check and call events
		glfwPollEvents();
		Do_Movement();

		// Clear the colorbuffer
		glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		shader.Use();   // <-- Don't forget this one!

		//Set View Postion
		GLint viewPosLoc = glGetUniformLocation(shader.Program, "viewPos");
		glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);

		calcLight(shader);

		// Set material properties
		glUniform1f(glGetUniformLocation(shader.Program, "material.shininess"), 32.0f);
		// Transformation matrices
		glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
		glm::mat4 view = camera.GetViewMatrix();
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));

		//***DEATH-BEGIN
		if (camera.Position.y<-12&&camera.Position.x>-70){
			camera.Position.x = 1;
			camera.Position.y = 0.5;
			camera.Position.z = -0.6;
		}
		else if (camera.Position.y<-12 && camera.Position.x<-70){
			camera.Position.x = 30.3016;
			camera.Position.y = 20.4118;
			camera.Position.z = -180.9861;
		}


		///****MAGDY- DEATH BY KNIFES
		
		if (camera.Position.x<-36.5&&death){
			camera.Position.x = -27.5945;
			camera.Position.y = 11.0592;
			camera.Position.z = -190.8711;
		}
		///****MAGDY

		
		//***DEATH-END

		//***WIN-BEGIN
		if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
			glm::vec3 WinPos = glm::vec3(27.36914, 48.79321, -546.85455);
			glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
			glm::vec3 l = cameraVector - WinPos;

			float length = glm::length(l);

			if (length<4){
				std::cout << "You won!" << std::endl;
			}
		}
		//***WIN-END


		// Draw the loaded model
		glm::mat4 modelWorld;
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelWorld));
		glActiveTexture(GL_TEXTURE3); // We already have 3 texture units active (in this shader) so set the skybox as the 4th texture unit (texture units are 0 based so index number 3)
		glUniform1i(glGetUniformLocation(shader.Program, "skybox"), 3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
		level.Draw(shader);

		// Draw the loaded model
		glm::mat4 modelPyramid;
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelPyramid));
		glActiveTexture(GL_TEXTURE3); // We already have 3 texture units active (in this shader) so set the skybox as the 4th texture unit (texture units are 0 based so index number 3)
		glUniform1i(glGetUniformLocation(shader.Program, "skybox"), 3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
		pyramid.Draw(shader);

		// Draw the loaded model
		glm::mat4 modelReflection;
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelReflection));
		glActiveTexture(GL_TEXTURE3); // We already have 3 texture units active (in this shader) so set the skybox as the 4th texture unit (texture units are 0 based so index number 3)
		glUniform1i(glGetUniformLocation(shader.Program, "skybox"), 3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
		reflect.Draw(shader);

		// Draw the loaded model
		glm::mat4 modelImprove;
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelReflection));
		glActiveTexture(GL_TEXTURE3); // We already have 3 texture units active (in this shader) so set the skybox as the 4th texture unit (texture units are 0 based so index number 3)
		glUniform1i(glGetUniformLocation(shader.Program, "skybox"), 3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
		improve.Draw(shader);

		

		//***KEY-BEGIN
		if (!pickedUp1){
			glm::mat4 modelKey1;
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
			glm::vec3 KeyPos = glm::vec3(36.72186, 27.03173, -200.44373);
			glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
			glm::vec3 l = cameraVector - KeyPos;

			float length = glm::length(l);		
			
				if (length<3){
					pickedUp1 = true;
				}
			}

			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKey1));
			key1.Draw(shader);
		}
		if (!pickedUp2){
			glm::mat4 modelKey2;
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
				glm::vec3 KeyPos = glm::vec3(337.3363, -8.44452, -196.17842);
				glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
				glm::vec3 l = cameraVector - KeyPos;

				float length = glm::length(l);

				if (length<3){
					pickedUp2 = true;
				}
			}
			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKey2));
			key2.Draw(shader);
		}
		if (!pickedUp3){
			glm::mat4 modelKey3;
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
				glm::vec3 KeyPos = glm::vec3(-105.57578, 9.80817, -192.29842);
				glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
				glm::vec3 l = cameraVector - KeyPos;

				float length = glm::length(l);

				if (length<3){
					pickedUp3 = true;
				}
			}
			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKey3));
			key3.Draw(shader);
		}

		if (!used1&&pickedUp1){
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
				glm::vec3 KeyHolePos = glm::vec3(25.8637, 19.96331, -189.58636);
				glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
				glm::vec3 l = cameraVector - KeyHolePos;

				float length = glm::length(l);

				if (length<1){
					used1 = true;
					spinFrontKnife = false;
				}
			}
		}

		if (!used2&&pickedUp2){
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
				glm::vec3 KeyHolePos = glm::vec3(24.52287, 19.96331, -189.58636);
				glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
				glm::vec3 l = cameraVector - KeyHolePos;

				float length = glm::length(l);

				if (length<1){
					used2 = true;
					spinMiddleKnife = false;
				}
			}
		}

		if (!used3&&pickedUp3){
			if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
				glm::vec3 KeyHolePos = glm::vec3(23.24703, 19.96331, -189.58636);
				glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
				glm::vec3 l = cameraVector - KeyHolePos;

				float length = glm::length(l);

				if (length<1){
					used3 = true;
					spinLastKnife = false;
				}
			}
		}


		if (used1){
			glm::mat4 modelKeyinHole1;
			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKeyinHole1));
			KeyinHole1.Draw(shader);
		}
		if (used2){
			glm::mat4 modelKeyinHole2;
			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKeyinHole2));
			KeyinHole2.Draw(shader);
		}
		if (used3){
			glm::mat4 modelKeyinHole3;
			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKeyinHole3));
			KeyinHole3.Draw(shader);
		}
		//***KEY-END


		//***FRIST SWITCH-BEGIN
		

		if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
			glm::vec3 SwitchPos = glm::vec3(11.3, 1.06, -73.4);
			glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
			glm::vec3 l = cameraVector - SwitchPos;

			float length = glm::length(l);

			translation = glm::vec3(-11.3, -1.06, 73.4);
			rotation = glm::vec3(1, 0, 0);
			rotationangle = 3.f*deltaTime;
			if (length<4){
				modelSwitch1 = glm::translate(modelSwitch1, glm::vec3(-translation.x, -translation.y, -translation.z));
				modelSwitch1 = glm::rotate(modelSwitch1, rotationangle, rotation);
				modelSwitch1 = glm::translate(modelSwitch1, translation);
				if (moveDoor1<17){
					translation = glm::vec3(0, 0.05, 0);
					modelDoor1 = glm::translate(modelDoor1, glm::vec3(translation.x, translation.y, translation.z));
					moveDoor1 += 0.05;
				}

			}
		}

		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelSwitch1));
		Firstswitch.Draw(shader);

		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelDoor1));
		FirstDoor.Draw(shader);

		//***FIRST SWITCH-END

		//***SECOUND SWITCH-BEGIN
		

		if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS){
			glm::vec3 SwitchPos = glm::vec3(317, -6.55, -174.14);
			glm::vec3 cameraVector = glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z);
			glm::vec3 l = cameraVector - SwitchPos;

			float length = glm::length(l);

			translation = glm::vec3(-317, 6.55, 174.14);
			rotation = glm::vec3(1, 0, 0);
			rotationangle = 3.f*deltaTime;
			if (length<4){
				modelSwitch2 = glm::translate(modelSwitch2, glm::vec3(-translation.x, -translation.y, -translation.z));
				modelSwitch2 = glm::rotate(modelSwitch2, rotationangle, rotation);
				modelSwitch2 = glm::translate(modelSwitch2, translation);
				if (moveDoor2<2.5){
					translation = glm::vec3(0, 0.05, 0);
					modelDoor2 = glm::translate(modelDoor2, glm::vec3(translation.x, translation.y, translation.z));
					moveDoor2 += 0.05;
				}

			}
		}

		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelSwitch2));
		Secoundswitch.Draw(shader);

		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelDoor2));
		SecoundDoor.Draw(shader);

		//***SECOUND SWITCH-END

		//***KNIFES-BEGIN
		if (camera.Position.x<-36.5){
			translation = glm::vec3(0.2, 0, 0);
			if (NegativeX1){
				modelKnife1 = glm::translate(modelKnife1, -translation);
				moveknife1 += 0.2;
				if (moveknife1 > 91){
					NegativeX1 = false;
				}
			}
			else{
				modelKnife1 = glm::translate(modelKnife1, translation);
				moveknife1 -= 0.2;
				if (moveknife1 < 45){
					NegativeX1 = true;
				}
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKnife1));
		kinfe1.Draw(shader);

		if (camera.Position.x<-36.5){
			translation = glm::vec3(0.1, 0, 0);
			if (NegativeX2){
				modelKnife2 = glm::translate(modelKnife2, -translation);
				moveknife2 += 0.1;
				if (moveknife2 > 91){
					NegativeX2 = false;
				}
			}
			else{
				modelKnife2 = glm::translate(modelKnife2, translation);
				moveknife2 -= 0.1;
				if (moveknife2 < 45){
					NegativeX2 = true;
				}
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKnife2));
		kinfe2.Draw(shader);


		if (camera.Position.x<-36.5){
			translation = glm::vec3(0.2, 0, 0);
			if (NegativeX3){
				modelKnife3 = glm::translate(modelKnife3, -translation);
				moveknife3 += 0.2;
				if (moveknife3 > 91){
					NegativeX3 = false;
				}
			}
			else{
				modelKnife3 = glm::translate(modelKnife3, translation);
				moveknife3 -= 0.2;
				if (moveknife3 < 45){
					NegativeX3 = true;
				}
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKnife3));
		kinfe3.Draw(shader);


		if (camera.Position.x<-36.5){
			translation = glm::vec3(0.1, 0, 0);
			if (NegativeX4){
				modelKnife4 = glm::translate(modelKnife4, -translation);
				moveknife4 += 0.1;
				if (moveknife4 > 91){
					NegativeX4 = false;
				}
			}
			else{
				modelKnife4 = glm::translate(modelKnife4, translation);
				moveknife4 -= 0.1;
				if (moveknife4 < 45){
					NegativeX4 = true;
				}
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelKnife4));
		kinfe4.Draw(shader);
		//***KNIFES-END

		//***MAIN KNIFES-BEGIN
		if (spinFrontKnife){
			translation = glm::vec3(-24.5, -25, 243);
			rotation = glm::vec3(0, 0, 1);
			rotationangle = 10.f*deltaTime;
			modelFrontKnife = glm::translate(modelFrontKnife, glm::vec3(-translation.x, -translation.y, -translation.z));
			modelFrontKnife = glm::rotate(modelFrontKnife, rotationangle, rotation);
			modelFrontKnife = glm::translate(modelFrontKnife, translation);
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelFrontKnife));
		frontkinfe.Draw(shader);
		
		if (spinMiddleKnife){
			translation = glm::vec3(-24.5, -25, 247);
			rotation = glm::vec3(0, 0, 1);
			rotationangle = 10.f*deltaTime;
			modelMiddleKnife = glm::translate(modelMiddleKnife, glm::vec3(-translation.x, -translation.y, -translation.z));
			modelMiddleKnife = glm::rotate(modelMiddleKnife, rotationangle, rotation);
			modelMiddleKnife = glm::translate(modelMiddleKnife, translation);
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelMiddleKnife));
		middlekinfe.Draw(shader);

		if (spinLastKnife){
			translation = glm::vec3(-24.5, -25, 252);
			rotation = glm::vec3(0, 0, 1);
			rotationangle = 10.f*deltaTime;
			modelLastKnife = glm::translate(modelLastKnife, glm::vec3(-translation.x, -translation.y, -translation.z));
			modelLastKnife = glm::rotate(modelLastKnife, rotationangle, rotation);
			modelLastKnife = glm::translate(modelLastKnife, translation);
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelLastKnife));
		lastkinfe.Draw(shader);

		//***MAIN KNIFES-END

		//***END DOOR-BEGIN
		if (camera.Position.z < -499){
			if (moveDoor3<62.89812){
				translation = glm::vec3(0, 0.1, 0);
				modelDoor3 = glm::translate(modelDoor3, glm::vec3(translation.x, translation.y, translation.z));
				moveDoor3 += 0.1;
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelDoor3));
		EndDoor.Draw(shader);


		if (camera.Position.z < -321.82367){
			if (moveDoor4<62.89812){
				translation = glm::vec3(0, 0.1, 0);
				modelFrontDoor = glm::translate(modelFrontDoor, glm::vec3(translation.x, translation.y, translation.z));
				moveDoor4 += 0.1;
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelFrontDoor));
		FrontDoor.Draw(shader);


		if (camera.Position.z < -407.82523){
			if (moveDoor5<62.89812){
				translation = glm::vec3(0, 0.1, 0);
				modelMiddleDoor = glm::translate(modelMiddleDoor, glm::vec3(translation.x, translation.y, translation.z));
				moveDoor5 += 0.1;
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelMiddleDoor));
		MiddleDoor.Draw(shader);


		if (camera.Position.z < -168.11334){
			if (moveDoor6<28.02253){
				translation = glm::vec3(0, 0.1, 0);
				modelEntranceDoor = glm::translate(modelEntranceDoor, glm::vec3(translation.x, translation.y, translation.z));
				moveDoor6 += 0.1;
			}
		}
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelEntranceDoor));
		EntranceDoor.Draw(shader);
		//***END DOOR-END


		//***ANIMATION-BEGIN
		translation = glm::vec3(-31, -28, 207);
		rotation = glm::vec3(0, 1, 0);
		float rotationangle = 5.f*deltaTime;
		modelHead = glm::translate(modelHead, glm::vec3(-translation.x, -translation.y, -translation.z));
		modelHead = glm::rotate(modelHead, rotationangle, rotation);
		modelHead = glm::translate(modelHead, translation);

		glm::vec3 Savetranslation;
		if (up){
			Savetranslation = glm::vec3(0,0.05,0);
			modelHead = glm::translate(modelHead, Savetranslation);
			move += 0.05;
			if (move>40){
				up = false;
			}
		}
		else{
			Savetranslation = glm::vec3(0,-0.05, 0);
			modelHead = glm::translate(modelHead, Savetranslation);
			move -= 0.05;
			if (move<28){
				up = true;
			}
		}
		
		glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelHead));
		head.Draw(shader);
		

		translation = glm::vec3(-31, -25, 207);
		rotation = glm::vec3(0, 1, 0);
			rotationangle = -0.5f*deltaTime;
			modelBody = glm::translate(modelBody, glm::vec3(-translation.x, -translation.y, -translation.z));
			modelBody = glm::rotate(modelBody, rotationangle, rotation);
			modelBody = glm::translate(modelBody, translation);
			modelBody = glm::translate(modelBody, Savetranslation);

			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelBody));
			body.Draw(shader);


			translation = glm::vec3(-31, -24, 207);
			rotation = glm::vec3(0, 1, 0);
			rotationangle = 3.f*deltaTime;
			modelHand = glm::translate(modelHand, glm::vec3(-translation.x, -translation.y, -translation.z));
			modelHand = glm::rotate(modelHand, rotationangle, rotation);
			modelHand = glm::translate(modelHand, translation);
			modelHand = glm::translate(modelHand, Savetranslation);

			glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(modelHand));
			hand.Draw(shader);

			//***ANIMATION-END



		// Swap the buffers

		// Draw skybox as last
		glDepthFunc(GL_LEQUAL);  // Change depth function so depth test passes when values are equal to depth buffer's content
		skyboxShader.Use();
		view = glm::mat4(glm::mat3(camera.GetViewMatrix()));	// Remove any translation component of the view matrix
		glUniformMatrix4fv(glGetUniformLocation(skyboxShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
		glUniformMatrix4fv(glGetUniformLocation(skyboxShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
		// skybox cube
		glBindVertexArray(skyboxVAO);
		glActiveTexture(GL_TEXTURE0);
		glUniform1i(glGetUniformLocation(shader.Program, "skybox"), 0);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
		glDrawArrays(GL_TRIANGLES, 0, 36);
		glBindVertexArray(0);
		glDepthFunc(GL_LESS); // Set depth function back to default

		glfwSwapBuffers(window);
	}

	glfwTerminate();
	return 0;
}

void calcLight(Shader shader){
	//SpotLight
	GLint spotlightPosLoc = glGetUniformLocation(shader.Program, "spotlight.position");
	GLint spotlightSpotdirLoc = glGetUniformLocation(shader.Program, "spotlight.direction");
	GLint spotlightSpotCutOffLoc = glGetUniformLocation(shader.Program, "spotlight.cutOff");
	GLint spotlightpower = glGetUniformLocation(shader.Program, "spotlight.power");
	glUniform3f(spotlightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
	glUniform3f(spotlightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
	glUniform1f(spotlightSpotCutOffLoc, 40.0f);
	// Set lights properties
	glUniform3f(glGetUniformLocation(shader.Program, "spotlight.ambient"), 0.1f, 0.1f, 0.1f);
	// We set the diffuse intensity a bit higher; note that the right lighting conditions differ with each lighting method and environment.
	// Each environment and lighting type requires some tweaking of these variables to get the best out of your environment.
	glUniform3f(glGetUniformLocation(shader.Program, "spotlight.diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "spotlight.specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "spotlight.constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "spotlight.linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "spotlight.quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "spotlight.power"), 20.0f);


	//PointLight 1
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[0].position"), pointLightPositions[0].x, pointLightPositions[0].y, pointLightPositions[0].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[0].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[0].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[0].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[0].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[0].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[0].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[0].power"), 10.0f);

	//PointLight 2
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[1].position"), pointLightPositions[1].x, pointLightPositions[1].y, pointLightPositions[1].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[1].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[1].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[1].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[1].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[1].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[1].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[1].power"), 10.0f);

	//PointLight 3
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[2].position"), pointLightPositions[2].x, pointLightPositions[2].y, pointLightPositions[2].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[2].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[2].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[2].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[2].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[2].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[2].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[2].power"), 10.0f);

	//PointLight 4
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[3].position"), pointLightPositions[3].x, pointLightPositions[3].y, pointLightPositions[3].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[3].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[3].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[3].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[3].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[3].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[3].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[3].power"), 10.0f);


	//PointLight 5
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[4].position"), pointLightPositions[4].x, pointLightPositions[4].y, pointLightPositions[4].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[4].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[4].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[4].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[4].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[4].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[4].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[4].power"), 10.0f);

	//PointLight 6
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[5].position"), pointLightPositions[5].x, pointLightPositions[5].y, pointLightPositions[5].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[5].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[5].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[5].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[5].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[5].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[5].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[5].power"), 100.0f);

	//PointLight 7
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[6].position"), pointLightPositions[6].x, pointLightPositions[6].y, pointLightPositions[6].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[6].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[6].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[6].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[6].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[6].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[6].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[6].power"), 20.0f);

	//PointLight 8
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[7].position"), pointLightPositions[7].x, pointLightPositions[7].y, pointLightPositions[7].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[7].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[7].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[7].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[7].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[7].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[7].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[7].power"), 20.0f);

	//PointLight 9
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[8].position"), pointLightPositions[8].x, pointLightPositions[8].y, pointLightPositions[8].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[8].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[8].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[8].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[8].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[8].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[8].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[8].power"), 20.0f);

	//PointLight 10
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[9].position"), pointLightPositions[9].x, pointLightPositions[9].y, pointLightPositions[9].z);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[9].ambient"), 0.1f, 0.1f, 0.1f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[9].diffuse"), 0.8f, 0.8f, 0.8f);
	glUniform3f(glGetUniformLocation(shader.Program, "pointlight[9].specular"), 1.0f, 1.0f, 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[9].constant"), 1.0f);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[9].linear"), 0.09);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[9].quadratic"), 0.032);
	glUniform1f(glGetUniformLocation(shader.Program, "pointlight[9].power"), 20.0f);

}

// Loads a cubemap texture from 6 individual texture faces
// Order should be:
// +X (right)
// -X (left)
// +Y (top)
// -Y (bottom)
// +Z (front) 
// -Z (back)
GLuint loadCubemap(vector<const GLchar*> faces)
{
	GLuint textureID;
	glGenTextures(1, &textureID);

	int width, height;
	unsigned char* image;

	glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
	for (GLuint i = 0; i < faces.size(); i++)
	{
		image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
		SOIL_free_image_data(image);
	}
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

	return textureID;
}

#pragma region "User input"

// Moves/alters the camera positions based on user input
void Do_Movement()
{
	// Camera controls
	if (keys[GLFW_KEY_W])
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (keys[GLFW_KEY_S])
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (keys[GLFW_KEY_A])
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (keys[GLFW_KEY_D])
		camera.ProcessKeyboard(RIGHT, deltaTime);
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
		glfwSetWindowShouldClose(window, GL_TRUE);

	if (action == GLFW_PRESS)
		keys[key] = true;
	else if (action == GLFW_RELEASE)
		keys[key] = false;
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}

	GLfloat xoffset = xpos - lastX;
	GLfloat yoffset = lastY - ypos;

	lastX = xpos;
	lastY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}

#pragma endregion