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

    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "Project Helios", 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);

	// Setup some OpenGL options
	glEnable(GL_DEPTH_TEST);

// Shaders - - - -

	// Setup and compile our shaders
	Shader lightingShader("resources/shaders/lighting.vs", "resources/shaders/lighting.frag");
    Shader lampShader("resources/shaders/lamp.vs", "resources/shaders/lamp.frag");

// Models - - - -


	while(KEY!=GLFW_KEY_ENTER)
	{
	    glfwSetWindowTitle(window, "Home");

        glfwPollEvents();
	    // Clear the colorbuffer
	    glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
	    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glfwSwapBuffers(window);
	}

    glfwSetWindowTitle(window, "Project Helios");

    // Load Models
    Model Map("resources/models/Mapa.obj");
    Model Lamp("resources/models/Lamp.obj");
    Model Bat1("resources/models/Huge Battery.obj");
    Model Bat2("resources/models/Huge Battery.obj");
    Model Bat3("resources/models/Huge Battery.obj");
    Model Bat4("resources/models/Huge Battery.obj");
    Model Keycard("resources/models/Keycard.obj");
    Model KeyExit("resources/models/Keycard.obj");
    Model Gun("resources/models/The Gun.obj");

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


//LOOP - - - -
	// Game loop
	while(!glfwWindowShouldClose(window))
	{
        // Set frame time
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

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

        // Clear the colorbuffer
        glClearColor(0.0f, 0.2f, 0.8f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        lampShader.Use();

        // Transformation matrices
        glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        glUniformMatrix4fv(glGetUniformLocation(lampShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glUniformMatrix4fv(glGetUniformLocation(lampShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniform3f(glGetUniformLocation(lampShader.Program, "lightColor"),  lightColor.x, lightColor.y, lightColor.z);


        lightingShader.Use();

        // Set lights properties
        glUniform3f(glGetUniformLocation(lightingShader.Program, "light.position"), lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(glGetUniformLocation(lightingShader.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);
        glUniform3f(glGetUniformLocation(lightingShader.Program, "light.color"), lightColor.x, lightColor.y, lightColor.z);
        glUniform3f(glGetUniformLocation(lightingShader.Program, "light.ambient"), 0.1f, 0.1f, 0.1f);

        // Transformation matrices
        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));


        // Draw the loaded model
        glm::mat4 model_map;
        glm::mat4 model_lamp;
        glm::mat4 model_bat1;
        glm::mat4 model_bat2;
        glm::mat4 model_bat3;
        glm::mat4 model_bat4;
        glm::mat4 model_key;
        glm::mat4 model_keyExit;
        glm::mat4 model_gun;


        // Prepare Matrices
        model_lamp = glm::translate(model_lamp, glm::vec3(0.0f, 3.93f, 0.0f));
        model_lamp = glm::rotate(model_lamp, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));

        model_bat1 = glm::translate(model_bat1, glm::vec3(6.5f, 0.0f, 4.0f));
        model_bat1 = glm::rotate(model_bat1, glm::radians(-135.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        model_bat2 = glm::translate(model_bat2, glm::vec3(6.5f, 0.0f, -4.0f));
        model_bat2 = glm::rotate(model_bat2, glm::radians(-45.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        model_bat3 = glm::translate(model_bat3, glm::vec3(6.5f, 0.0f, 0.0f));
        model_bat3 = glm::rotate(model_bat3, glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        model_bat4 = glm::translate(model_bat4, glm::vec3(-6.5f, 0.0f, 4.0f));
        model_bat4 = glm::rotate(model_bat4, glm::radians(135.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        model_key = glm::translate(model_key, glm::vec3(1.6f, 1.32f, -0.3f));
        model_key = glm::rotate(model_key, glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        model_keyExit = glm::translate(model_keyExit, glm::vec3(-6.25f, 1.2f, -4.99f));
        model_keyExit = glm::rotate(model_keyExit, glm::radians(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));


        // Draw
        lampShader.Use();
        glUniformMatrix4fv(glGetUniformLocation(lampShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_lamp));
        Lamp.Draw(lampShader);

        lightingShader.Use();
        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_map));
        glUniform3f(glGetUniformLocation(lightingShader.Program, "material.ambient"), 1.0f, 1.0f, 1.0f);
        glUniform3f(glGetUniformLocation(lightingShader.Program, "material.diffuse"), 0.8f, 0.8f, 0.8f);
        glUniform3f(glGetUniformLocation(lightingShader.Program, "material.specular"), 0.7f, 0.7f, 0.7f);
        glUniform1f(glGetUniformLocation(lightingShader.Program, "material.shininess"), 32.0f);
        Map.Draw(lightingShader);

        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_bat1));
        glUniform3f(glGetUniformLocation(lightingShader.Program, "material.specular"), 1.0f, 1.0f, 1.0f);
        glUniform1f(glGetUniformLocation(lightingShader.Program, "material.shininess"), 2.0f);
        Bat1.Draw(lightingShader);

        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_bat2));
        Bat2.Draw(lightingShader);

        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_bat3));
        Bat3.Draw(lightingShader);

        glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_bat4));
        Bat4.Draw(lightingShader);


        // Verify if the key was taken
        if(enableKey){
            glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_key));
            glUniform3f(glGetUniformLocation(lightingShader.Program, "material.diffuse"), 1.0f, 1.0f, 1.0f);
            glUniform3f(glGetUniformLocation(lightingShader.Program, "material.specular"), 1.0f, 1.0f, 1.0f);
            glUniform1f(glGetUniformLocation(lightingShader.Program, "material.shininess"), 8.0f);
            Keycard.Draw(lightingShader);
        }

        if(enableExitKey){
            glUniformMatrix4fv(glGetUniformLocation(lightingShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model_keyExit));
            KeyExit.Draw(lightingShader);
        }

        colorOffsetR = 0.5 * sin(glm::radians(alpha)) + 0.5;
        colorOffsetG = 0.5 * sin(glm::radians(2*alpha)) + 0.5;
        colorOffsetB = 0.5 * sin(glm::radians(4*alpha)) + 0.5;
        //lightColor = glm::vec3(colorOffsetR, colorOffsetG, colorOffsetB);

        if(alpha > 360)
            alpha = 0;
        else
            alpha += 0.05;

        // Swap the buffers
		glfwSwapBuffers(window);
	}

	glfwTerminate();
	return 0;
}
void display()
{   glClearColor(0.6, 0.75, 0.85, 1); // sky color is light blue
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	/* Clear The Screen And The Depth Buffer */
	if(view==0)
	{   
		display1();
	}

  else if (view==1)
	{
	display2();
   }
   else if(view==2)
   {
   //  Erase the window and the depth buffer
   
   glClearColor(0.6, 0.75, 0.85, 1); // sky color is light blue
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    //  Enable Z-buffering in OpenGL
   glEnable(GL_DEPTH_TEST);
   glCullFace(GL_BACK);
  
   glLoadIdentity();
    
   // Set the look at position for perspective projection
      		gluLookAt(
			Ex,      Ey,      Ez,
			Ex + lx, Ey, Ez+lz,
			0.0,    1,    0.0);
   
    
  glShadeModel(GL_SMOOTH);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glPushMatrix();
  glTranslatef(	Ex + lx, 0, Ez+lz-4);
  Sky(1000); 
  glPopMatrix();
  glDepthMask(GL_TRUE);
  glEnable(GL_DEPTH_TEST);
  
    //  Light switch
   if (light)
   {
        //  Translate intensity to color vectors
        float Ambient[]   = {0.01*ambient ,0.01*ambient ,0.01*ambient ,1.0};
        float Diffuse[]   = {0.01*diffuse ,0.01*diffuse ,0.01*diffuse ,1.0};
        float Specular[]  = {0.01*specular,0.01*specular,0.01*specular,1.0};
        //  Light position
      float Position[]={0,1000,5000};
        //  Draw light position as ball (still no lighting here)
        glColor3f(1,1,1);
         ball(Position[0],Position[1],Position[2] , 0.2);
        //  OpenGL should normalize normal vectors
        glEnable(GL_NORMALIZE);
        //  Enable lighting
        glEnable(GL_LIGHTING);
        //  glColor sets ambient and diffuse color materials
        glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
        glEnable(GL_COLOR_MATERIAL);
        //  Enable light 0
        glEnable(GL_LIGHT0);
        //  Set ambient, diffuse, specular components and position of light 0
        glLightfv(GL_LIGHT0,GL_AMBIENT ,Ambient);
        glLightfv(GL_LIGHT0,GL_DIFFUSE ,Diffuse);
        glLightfv(GL_LIGHT0,GL_SPECULAR,Specular);
        glLightfv(GL_LIGHT0,GL_POSITION,Position);
        
   }
   else
     glDisable(GL_LIGHTING);
   
	if(opt==1)
	{
		 glPushMatrix();
         glTranslatef(	Ex + lx,0, Ez+lz-4);
         glRotatef(90, 1, 0, 0);
         glRotatef(180, 0, 1, 0);
         glColor3f(1,1,1);
		 glCallList(car1);
		 glPopMatrix();
		  int E=Ez+lz-4;
		 if(E<=40)
		 {
		 	Ex=0;
		 	Ez=1000;
		 	lx=0;
		 	lz=-1;
		 }
		
	}
	else if(opt==2)
	{
		 glPushMatrix();
         glTranslatef(	Ex + lx, 0, Ez+lz-4);
         glRotatef(90, 1, 0, 0);
         glRotatef(180, 0, 1, 0);
         glColor3f(1,1,1);
		 glCallList(car2);
		 glPopMatrix();
		   int E=Ez+lz-4;
		 if(E<=40)
		 {
		 	Ex=0;
		 	Ez=1000;
		 	lx=0;
		 	lz=-1;
		 }
	}
	cylinder();
    Road();
    
     
	for(i=0; i<=1000; i+=40)
	{
	glPushMatrix();
    glTranslated(-50,10,i);
    glScaled(50,50,50);
    Tree();
    glPopMatrix();
  }
    for(i=0; i<=1000; i+=40)
	{
	glPushMatrix();
    glTranslated(170,10,i);
    glScaled(50,50,50);
    Tree();
    glPopMatrix();
  }
    for(i=0; i<=1000; i+=60)
	{
	glPushMatrix();
    glTranslated(-70,-10,i);
    glScaled(2,10,10);
    Lamp();
    glPopMatrix();
  }
  for(i=0; i<=1000; i+=60)
	{
	glPushMatrix();
    glTranslated(70,-10,i);
    glRotated(90,0,1,0);
    glScaled(2,10,10);
    Lamp();
    glPopMatrix();
  }
  for(i=-60; i<=1000; i+=60)
	{
	glPushMatrix();
    glTranslated(-38,86.5,i+43);
    glScaled(5,5,5);
    lights();
    glPopMatrix();
  }
   for(i=-60; i<=1000; i+=60)
	{
	glPushMatrix();
    glTranslated(55,87,i+30);
    glScaled(5,5,5);
    lights();
    glPopMatrix();
  }
   for(i=0; i<=1000; i+=400)
	{
	glPushMatrix();
    glTranslated(-50,-10,i);
    glScaled(10,10,10);
    trafficsignal();
    glPopMatrix();
    }
     for(i=0; i<=1000; i+=400)
	{
	glPushMatrix();
    glTranslated(0,0,i+40);
    zebracrossing();
    glPopMatrix();
    }
     for(i=-60; i<=1000; i+=100)
	{
	glPushMatrix();
    glTranslated(200,-15,i+30);
    glScaled(10,15,10);
    buildings();
    glPopMatrix();
  }
   for(i=-60; i<=1000; i+=100)
	{
	glPushMatrix();
    glTranslated(-250,-15,i+30);
    glScaled(10,15,10);
    buildings();
    glPopMatrix();
  }
   for(i=-60; i<=1000; i+=100)
	{
	glPushMatrix();
    glTranslated(350,-15,i+30);
    glScaled(10,30,10);
    buildings();
    glPopMatrix();
  }
   for(i=-60; i<=1000; i+=100)
	{
	glPushMatrix();
    glTranslated(-400,-15,i+30);
    glScaled(10,30,10);
    buildings();
    glPopMatrix();
  }
     
   for(e=0; e<=1000; e+=155)
	{
	  if(e%2==0)
	{
		glPushMatrix();
		glTranslated(-16,-10,e);
		glScaled(6,6,6);
        roadpylon();
        glPopMatrix();
    }
    else
	{
		glPushMatrix();
		glTranslated(16,-10,e);
        glScaled(6,6,6);
        roadpylon();
        glPopMatrix();
    }
    } 
    for(i=0; i<=1000; i+=400)
	{
	glPushMatrix();
    glTranslated(65,0,i);
    glScaled(10,10,10);
    roadsign();
    glPopMatrix();
    }
  
  xpos = xpos + xvel;

if(xpos >= 30)
{
xvel = -xvel;
}
if( xpos <2)
{
xvel = -xvel;
}
  glPushMatrix();
  glTranslated(0,xpos,900);
  glRotated(-90,0,0,1);
  glRotated(-90,0,1,0);
  glScaled(2,2,2);
  glColor3f(1,1,1);
  glCallList(dragon);
  glPopMatrix();
   glWindowPos2i(5,5);
   glColor3f(1,1,1);
  Print("Speed=%f",1,jump);
 
}
   //  Render the scene and make it visible
   ErrCheck("display");
   glFlush();
   glutSwapBuffers();
}