#pragma once

#include "VulkanCommandBuffers.h"
#include "VulkanFramebuffers.h"
#include "VulkanInstanceManager.h"
#include "VulkanDeviceManager.h"
#include "VulkanDebugManager.h"
#include "VulkanPipeline.h"
#include "VulkanRenderPass.h"
#include "VulkanSwapChain.h"
#include "VulkanVertexBuffer.h"

#include <cstdint>
#include <string>
#include <vector>
#include <vulkan/vulkan_core.h>

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

static const std::vector<const char*> ValidationLayers = {
	"VK_LAYER_KHRONOS_validation"
};

struct FVulkanConfig
{
	bool		bValidationEnabled = true;
	bool		bVerboseLogging = false;
	GLFWwindow* Window = nullptr;
	// std::vector<Vertex> Vertices;
};

class VulkanContext
{
public:
	VulkanContext();
	~VulkanContext();

	VulkanInstanceManager InstanceManager;
	VulkanDebugManager	  DebugManager;
	VulkanDeviceManager	  DeviceManager;
	VulkanSwapChain		  SwapChain;
	VulkanPipeline		  GraphicsPipeline;
	VulkanRenderPass	  RenderPass;
	VulkanFramebuffers	  Framebuffers;
	VulkanVertexBuffer	  VertexBuffer;
	VulkanCommandBuffers  CommandBuffers;

	void Initialize(FVulkanConfig& InConfig, const std::vector<Vertex>& InVertices, const std::vector<uint16_t>& InIndices);
	void Cleanup();

	void CreateSurface(GLFWwindow* Window);

	void CreateDescriptorSetLayout();
	void CreateDescriptorPool();
	void CreateDescriptorSets();

	void CreateImage(
		uint32_t			  Width,
		uint32_t			  Height,
		VkFormat			  Format,
		VkImageTiling		  Tiling,
		VkImageUsageFlags	  Usage,
		VkMemoryPropertyFlags Properties,
		VkImage&			  Image,
		VkDeviceMemory&		  ImageMemory);
	void CreateTextureImage(const char* FileName);

	void CreateSyncObjects();

	void DrawFrame(bool bDrawImGui, uint32_t InVerticesSize, uint32_t InIndexSize);
	void UpdateUniformBuffer(uint32_t CurrentImage);

	void RecreateSwapChain();

	void InitImGui();
	void CleanupSwapChain();
	// void DrawFrame(ImDrawData* DrawData = nullptr);

	VkSurfaceKHR GetSurface() { return Surface; };

	void SetFramebufferResized(bool bResized) { bFramebufferResized = bResized; }

private:
	FVulkanConfig Config = {};
	VkSurfaceKHR  Surface = VK_NULL_HANDLE;

	std::vector<VkSemaphore> ImageAvailableSemaphores;
	std::vector<VkSemaphore> RenderFinishedSemaphores;
	std::vector<VkFence>	 InFlightFences;

	VkDescriptorPool ImGuiPool;

	VkDescriptorSetLayout		 DescriptorSetLayout;
	VkDescriptorPool			 DescriptorPool;
	std::vector<VkDescriptorSet> DescriptorSets;

	VkImage		   TextureImage;
	VkDeviceMemory TextureImageMemory;

	bool bFramebufferResized = false;
	bool bImGuiGlfwInitialized = false;
	bool bImGuiVulkanInitialized = false;

	const size_t MAX_FRAMES_IN_FLIGHT = 2;
	uint32_t	 CurrentFrame = 0;
};
