#pragma once

class VulkanInstanceManager;
class VulkanDebugManager;
class VulkanDeviceManager;
class VulkanSwapChain;
class VulkanPipeline;
class VulkanRenderPass;
class VulkanBuffers;
class VulkanFramebuffers;
class VulkanTexture;
class Vertex;

#include <cstdint>
#include <memory>
#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" };
static size_t						  MAX_FRAMES_IN_FLIGHT = 2;

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

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

	std::unique_ptr<VulkanInstanceManager> InstanceManager;
	std::unique_ptr<VulkanDebugManager>	   DebugManager;
	std::unique_ptr<VulkanDeviceManager>   DeviceManager;
	std::unique_ptr<VulkanSwapChain>	   SwapChain;
	std::unique_ptr<VulkanPipeline>		   GraphicsPipeline;
	std::unique_ptr<VulkanRenderPass>	   RenderPass;
	std::unique_ptr<VulkanFramebuffers>	   Framebuffers;
	std::unique_ptr<VulkanBuffers>		   Buffers;
	std::unique_ptr<VulkanTexture>		   Textures;
	// std::unique_ptr<VulkanDepthPass>	   DepthPass;

	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 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;
	// VkImageView	   TextureImageView;
	// VkSampler	   TextureSampler;
	//
	// VkImage		   DepthImage;
	// VkDeviceMemory DepthImageMemory;
	// VkImageView	   DepthImageView;

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

	uint32_t CurrentFrame = 0;
};
