#include <GLFW/glfw3.h>

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

#include "asset/object/model.h"
#include "assimp/Importer.hpp"
#include "assimp/postprocess.h"
#include "assimp/scene.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);

  std::shared_ptr<veng::Graphics> graphics =
      std::make_shared<veng::Graphics>(&window);

  Assimp::Importer assetImporter;

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

  if (scene == nullptr || !scene->HasMeshes()) {
    spdlog::error(assetImporter.GetErrorString());
    return false;
  }

  veng::Model player(graphics);

  aiMesh* mesh = scene->mMeshes[0];
  for (std::uint32_t i = 0; i < mesh->mNumVertices; i++) {
    glm::vec2 uv = {mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y};
    player.vertices.emplace_back(
        glm::vec3{mesh->mVertices[i].x, mesh->mVertices[i].y,
                  mesh->mVertices[i].z},
        uv);
  }
  player.vertex_buffer = graphics->CreateVertexBuffer(player.vertices);

  for (std::uint32_t i = 0; i < mesh->mNumFaces; i++) {
    aiFace face = mesh->mFaces[i];
    for (unsigned int j = 0; j < face.mNumIndices; ++j) {
      player.indices.push_back(face.mIndices[j]);
    }
  }
  player.index_buffer = graphics->CreateIndexBuffer(player.indices);

  aiString string;
  if (scene->mMaterials[scene->mMeshes[0]->mMaterialIndex]->GetTexture(
          aiTextureType_DIFFUSE, 0, &string) != AI_SUCCESS) {
    std::cout << "No texture" << std::endl;
  }

  veng::Model wall(graphics);

  wall.vertices = {veng::Vertex{{-1.f, -1.f, 0.f}, {0.f, 1.f}},
                    veng::Vertex{{1.f, -1.f, 0.f}, {1.f, 1.f}},
                    veng::Vertex{{-1.f, 1.f, 0.f}, {0.f, 0.f}},
                    veng::Vertex{{1.f, 1.f, 0.f}, {1.f, 0.f}}};
  wall.vertex_buffer = graphics->CreateVertexBuffer(wall.vertices);

  wall.indices = {0, 3, 2, 0, 1, 3};
  wall.index_buffer = graphics->CreateIndexBuffer(wall.indices);

  player.transform =
      glm::rotate(glm::mat4(1.0f), glm::radians(0.f), glm::vec3(0.f, 1.f, 0.f));
  // player.transform = glm::scale(player.transform,
  // glm::vec3(50.f, 50.f, 50.f));

  wall.transform = glm::rotate(glm::mat4(1.0f), glm::radians(180.f),
                                glm::vec3(0.f, 0.f, 1.f));
  // plane.transform = glm::scale(plane.transform, glm::vec3(100.f, 100.f,
  // 100.f));

  glm::mat4 view =
      glm::lookAt(glm::vec3(0.f, 0.f, 10.f), glm::vec3(0.f, 0.f, 0.f),
                  glm::vec3(0.f, 1.f, 0.f));
  /*glm::mat4 projection =
      glm::perspective(glm::radians(150.f), 800.f / 600.f, 0.1f, 100.f);*/
  glm::mat4 projection = glm::ortho(-10.f, 10.f, -10.f, 10.f, -10.f, 10.f);
  graphics->SetViewProjection(view, projection);

  // player.material.texture =
  // graphics->CreateTexture("assets/paving-stones.jpg");

  auto texture = scene->GetEmbeddedTexture(string.C_Str());
  player.material.texture = graphics->CreateTexture(
      gsl::span{(std::uint8_t*)texture->pcData, texture->mWidth});
  wall.material.texture = graphics->CreateTexture("assets/copying-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;
      auto grater = (current_window_size.x > current_window_size.y)
                        ? current_window_size.x
                        : current_window_size.y;
      projection = glm::ortho(0.01f * (float)current_window_size.x / 2,
                              -0.01f * (float)current_window_size.x / 2,
                              -0.01f * (float)current_window_size.y / 2,
                              0.01f * (float)current_window_size.y / 2,
                              -1 * (float)grater, (float)grater);
      /*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);
    }

    // std::cout << glfwGetKey(window.GetHandle(), GLFW_KEY_W) << std::endl;

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

      player.transform =
          glm::rotate(player.transform, delta_time * glm::radians(60.f),
                      glm::vec3(0.f, 0.f, 1.f));
      graphics->RenderModel(player);

      wall.transform =
          glm::rotate(wall.transform, delta_time * glm::radians(-60.f),
                      glm::vec3(0.f, 1.f, 0.f));
      graphics->RenderModel(wall);

      graphics->EndFrame();
    }
  }

  return EXIT_SUCCESS;
}
