#include <GLFW/glfw3.h>

#include <glm/gtc/matrix_transform.hpp>
#include <iostream>

#include "assimp/Importer.hpp"
#include "assimp/postprocess.h"
#include "glfw/glfw_initialization.h"
#include "glfw/glfw_monitor.h"
#include "glfw/glfw_window.h"
#include "precomp.h"
#include "vulkan/graphics.h"

std::int32_t main(std::int32_t argc, gsl::zstring* argv) {
  const veng::GlfwInitialization _glfw;

  veng::Window window("Vulkan Engine", {800, 600});
  window.TryMoveToMonitor(0);

  veng::Graphics graphics(&window);

  Assimp::Importer assetImporter;

  const struct aiScene* scene = assetImporter.ReadFile(
      "assets/player.fbx", aiProcess_CalcTangentSpace | aiProcess_Triangulate |
              aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);

  if (nullptr == scene) {
    spdlog::error(assetImporter.GetErrorString());
    return false;
  }

  std::array<veng::Vertex, 4> vertices = {
      veng::Vertex{{-.5f, -.5f, 0.f}, {0.f, 1.f}},
      veng::Vertex{{.5f, -.5f, 0.f}, {1.f, 1.f}},
      veng::Vertex{{-.5f, .5f, 0.f}, {0.f, 0.f}},
      veng::Vertex{{.5f, .5f, 0.f}, {1.f, 0.f}}};
  veng::BufferHandle buffer = graphics.CreateVertexBuffer(vertices);

  std::array<std::uint32_t, 6> indices = {0, 3, 2, 0, 1, 3};
  veng::BufferHandle index_buffer = graphics.CreateIndexBuffer(indices);

  glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), glm::radians(45.f),
                                   glm::vec3(0.f, 1.f, 0.f));

  glm::mat4 rotation2 =
      glm::rotate(glm::mat4(1.0f), glm::radians(0.f), glm::vec3(0.f, 0.f, 1.f));

  glm::mat4 view = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, -2.f));
  glm::mat4 projection =
      glm::perspective(glm::radians(150.f), 800.f / 600.f, 0.1f, 100.f);
  graphics.SetViewProjection(view, projection);

  veng::TextureHandle texture =
      graphics.CreateTexture("assets/paving-stones.jpg");

  double last_time = glfwGetTime();

  glm::ivec2 window_size;
  while (!window.ShouldClose()) {
    glfwPollEvents();

    glm::ivec2 current_window_size = window.GetFramebufferSize();
    if (current_window_size != window_size && current_window_size.x != 0 && current_window_size.y != 0) {
      window_size = current_window_size;
      projection = glm::perspective(
          glm::radians(90.f),
          (float)current_window_size.x / (float)current_window_size.y, 0.1f,
          100.f);
      graphics.SetViewProjection(view, projection);
    }

    if (graphics.BeginFrame()) {
      double current_time = glfwGetTime();
      float delta_time = static_cast<float>(current_time - last_time);
      last_time = current_time;

      graphics.SetTexture(texture);
      rotation2 = glm::rotate(rotation2, delta_time * glm::radians(-360.f),
                              glm::vec3(0.f, 0.f, 1.f));
      graphics.SetModelMatrix(rotation2);
      graphics.RenderIndexedBuffer(buffer, index_buffer, indices.size());
      rotation = glm::rotate(rotation, delta_time * glm::radians(360.f),
                             glm::vec3(0.f, 0.f, 1.f));
      graphics.SetModelMatrix(rotation);
      graphics.RenderIndexedBuffer(buffer, index_buffer, indices.size());

      graphics.EndFrame();
    }
  }

  graphics.DestroyTexture(texture);
  graphics.DestroyBuffer(buffer);
  graphics.DestroyBuffer(index_buffer);

  return EXIT_SUCCESS;
}
