#include <glm/glm.hpp>
#include <vector>
#include <string>
#include "Window.h"
#include "Renderer/Renderer.h"
#include "Primitives/Skybox.h"
#include "Primitives/shader.h"
#include "Primitives/camera.h"
#include <imgui.h>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <chrono>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

#include "Application.h"
#include "Primitives/Scene.h"


void processInput( GLFWwindow* window);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
const int WIDTH = 800, HEIGHT = 600;
float deltaTime = 0.0f; ;	// Time between current frame and last frame
float lastFrame = 0.0f; // Time of last frame
bool firstMouse  = true;
float lastx = 400, lasty = 300;
Scene scene;

class LearnOpenGL : Application 
{
public: 
   void Run () override
   {
      // Create a window
      Window window(WIDTH,HEIGHT, "LearnOpenGL");
      
      IMGUI_CHECKVERSION();
      ImGui::CreateContext();
      ImGuiIO& io = ImGui::GetIO();(void)io;
      

      ImGui::StyleColorsDark();

      ImGui_ImplGlfw_InitForOpenGL(window.ptr(), true);
      ImGui_ImplOpenGL3_Init("#version 460");

      stbi_set_flip_vertically_on_load(true);
      Model backpack("../Models/backpack.obj");

      scene.entities.push_back(backpack);
      scene.MainCamera = Camera(glm::vec3(0.0f, 0.0f, 8.0f));




      std::vector<std::string> faces = {
          "../Textures/skybox/right.jpg",
          "../Textures/skybox/left.jpg",
          "../Textures/skybox/top.jpg",
          "../Textures/skybox/bottom.jpg",
          "../Textures/skybox/front.jpg",
          "../Textures/skybox/back.jpg"
      };
      scene.skybox.loadCubeTextures(faces);

      Renderer renderer = Renderer();
      renderer.resize(WIDTH, HEIGHT);
      renderer.Setup();




      //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      while(!window.shouldClose())
      {
         float currentFrame = glfwGetTime();
         deltaTime = currentFrame - lastFrame;
         lastFrame = currentFrame; 
         int fps = 1000 / (std::round(deltaTime/ 0.001) * 0.001);
         glfwPollEvents();
         processInput(window.ptr());
         renderer.Render(scene);


         ImGui_ImplOpenGL3_NewFrame();
         ImGui_ImplGlfw_NewFrame();
         ImGui::NewFrame();
         ImGui::ShowDemoWindow(nullptr);

         ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.6f));
         ImGui::Begin("##Performance");
         ImGui::Text("Frame time: %5.3f", deltaTime);
         ImGui::Text("FPS: %d", fps  );
         ImGui::End();
         ImGui::PopStyleColor();

         ImGui::Render();

         ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
 
         window.SwapBuffers();
      }

      scene.skybox.Destroy();

      ImGui_ImplOpenGL3_Shutdown();
      ImGui_ImplGlfw_Shutdown();
      ImGui::DestroyContext();

   }
};

void processInput( GLFWwindow* window)
{
   if(glfwGetKey(window,GLFW_KEY_ESCAPE) == GLFW_PRESS)
      glfwSetWindowShouldClose(window, true);

   const float CameraSpeed = 0.8f * deltaTime; 
   if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
      scene.MainCamera.ProcessKeyboard(FORWARD, CameraSpeed);
   if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
      scene.MainCamera.ProcessKeyboard(BACKWARD,CameraSpeed);
   if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
      scene.MainCamera.ProcessKeyboard(LEFT, CameraSpeed);
   if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
      scene.MainCamera.ProcessKeyboard(RIGHT,CameraSpeed);
}

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

    float xoffset = xpos - lastx;
    float yoffset = ypos - lasty;

    lastx = xpos;
    lasty - ypos;

    scene.MainCamera.ProcessMouseMovement(xoffset, yoffset);
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0,0, width,height);
   

}

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

int main() 
{
   printf("Hello OpenGL!\n");
   LearnOpenGL().Run();
   return 0;
}
