#include <GLFW/glfw3.h>

#include "precomp.h"
#include "vulkan/graphics.h"

namespace veng {

Graphics::Graphics(gsl::not_null<Window*> window) : window_(window) {
#if !defined(NDEBUG)
  validation_enabled_ = true;
#endif
  InitializeVulkan();
}

Graphics::~Graphics() {
  if (logical_device_ != VK_NULL_HANDLE) {
    vkDeviceWaitIdle(logical_device_);
    CleanupSwapChain();
    DestroyTexture(depth_texture_);

    if (texture_pool_ != VK_NULL_HANDLE)
      vkDestroyDescriptorPool(logical_device_, texture_pool_, nullptr);

    if (texture_set_layout_ != VK_NULL_HANDLE)
      vkDestroyDescriptorSetLayout(logical_device_, texture_set_layout_,
                                   nullptr);

    if (texture_sampler_ != VK_NULL_HANDLE)
      vkDestroySampler(logical_device_, texture_sampler_, nullptr);

    if (uniform_pool_ != VK_NULL_HANDLE)
      vkDestroyDescriptorPool(logical_device_, uniform_pool_, nullptr);

    for (Frame& frame : frames_) {
      DestroyBuffer(frame.uniform_buffer);

      if (frame.image_available_signal != VK_NULL_HANDLE)
        vkDestroySemaphore(logical_device_, frame.image_available_signal,
                           nullptr);

      if (frame.render_finished_signal != VK_NULL_HANDLE)
        vkDestroySemaphore(logical_device_, frame.render_finished_signal,
                           nullptr);

      if (frame.still_rendering_fence != VK_NULL_HANDLE)
        vkDestroyFence(logical_device_, frame.still_rendering_fence, nullptr);
    }

    if (uniform_set_layout_ != VK_NULL_HANDLE)
      vkDestroyDescriptorSetLayout(logical_device_, uniform_set_layout_,
                                   nullptr);

    if (command_pool_ != VK_NULL_HANDLE)
      vkDestroyCommandPool(logical_device_, command_pool_, nullptr);

    if (logical_device_ != VK_NULL_HANDLE)
      vkDestroyPipeline(logical_device_, pipeline_, nullptr);

    if (pipeline_layout_ != VK_NULL_HANDLE)
      vkDestroyPipelineLayout(logical_device_, pipeline_layout_, nullptr);

    if (render_pass_ != VK_NULL_HANDLE)
      vkDestroyRenderPass(logical_device_, render_pass_, nullptr);

    vkDestroyDevice(logical_device_, nullptr);
  }
  if (instance_ != VK_NULL_HANDLE) {
    if (surface_ != VK_NULL_HANDLE)
      vkDestroySurfaceKHR(instance_, surface_, nullptr);

    if (debug_messenger_ != VK_NULL_HANDLE)
      vkDestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr);

    vkDestroyInstance(instance_, nullptr);
  }
}

void Graphics::InitializeVulkan() {
  CreateInstance();
  SetupDebugMessenger();
  CreateSurface();
  PickPhysicalDevice();
  CreateLogicalDeviceAndQueues();
  CreateSwapChain();
  CreateImageViews();
  CreateRenderPass();
  CreateDescriptorSetLayouts();
  CreateGraphicsPipeline();
  CreateDepthResources();
  CreateFramebuffers();
  CreateCommandPool();
  CreateCommandBuffer();
  CreateSignals();
  CreateUniformBuffers();
  CreateDescriptorPools();
  CreateDescriptorSets();
  CreateTextureSampler();
  TransitionImageLayout(depth_texture_.image, VK_IMAGE_LAYOUT_UNDEFINED,
                        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}

}  // namespace veng
