#pragma once

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

#include <cstdint>
#include <vector>
#include <optional>

#include "Primitives.h"

struct FBufferConfig
{
	VkDevice		 Device;
	VkRenderPass	 RenderPass;
	VkPhysicalDevice PhysicalDevice;
	VkQueue			 GraphicsQueue;
};

struct FRecordBuffersParams
{
	VkCommandBuffer			   InCommandBuffer;
	uint32_t				   ImageIndex;
	VkBuffer				   InVertexBuffer;
	uint32_t				   InVerticesSize;
	VkBuffer				   InIndexBuffer;
	uint32_t				   InIndexSize;
	VkRenderPass			   RenderPass;
	VkExtent2D				   SwapChainExtent;
	VkPipeline				   GraphicsPipeline;
	std::vector<VkFramebuffer> SwapChainFramebuffers;
	VkDescriptorSet			   DescriptorSet;
	VkPipelineLayout		   PipelineLayout;
	bool					   bDrawImGui = false;
	// std::vector<Vertex>		   InVertices;
	// ImDrawData*				   DrawData = nullptr;
};

class VulkanBuffers
{
public:
	void Initialize(FBufferConfig InConfig);
	// VkDevice	 InDevice,
	// VkRenderPass InRenderPass);

	void Cleanup(size_t MAX_FRAMES_IN_FLIGHT);

	void CreateCommandPool(std::optional<uint32_t> GraphicsFamily);

	void CreateCommandBuffers(int FramesInFlight);

	void RecordCommandBuffer(FRecordBuffersParams& Params);

	VkCommandBuffer BeginSingleTimeCommands();
	void			EndSingleTimeCommands(VkCommandBuffer InCommandBuffer, VkQueue GraphicsQueue);

	void TransitionImageLayout(
		VkImage		  Image,
		VkFormat	  Format,
		VkImageLayout OldLayout,
		VkImageLayout NewLayout,
		bool		  bHasStencilComponent
	);

	void CopyBufferToImage(VkBuffer Buffer, VkImage Image, uint32_t Width, uint32_t Height);

	void
	CopyBuffer(VkQueue GraphicsQueue, VkBuffer SrcBuffer, VkBuffer DstBuffer, VkDeviceSize Size);
	void CreateBuffer(
		VkDeviceSize		  Size,
		VkBufferUsageFlags	  Usage,
		VkMemoryPropertyFlags Properties,
		VkBuffer&			  Buffer,
		VkDeviceMemory&		  BufferMemory
	);
	void CreateVertexBuffer(const std::vector<Vertex>& InVertices);
	void CreateIndexBuffer(const std::vector<uint16_t>& InIndices);
	void CreateUniformBuffers(const uint32_t MAX_FRAMES_IN_FLIGHT);

	uint32_t FindMemoryType(uint32_t TypeFilter, VkMemoryPropertyFlags Properties);

	std::vector<VkCommandBuffer> GetCommandBuffers() { return CommandBuffers; }
	VkCommandBuffer				 GetCommandBuffer(int i) { return CommandBuffers[i]; }
	VkCommandPool				 GetCommandPool() { return CommandPool; }
	VkBuffer					 GetVertexBuffer() { return VertexBuffer; }
	VkBuffer					 GetIndexBuffer() { return IndexBuffer; }
	std::vector<VkBuffer>		 GetUniformBuffers() { return UniformBuffers; }
	std::vector<void*>			 GetUniformBuffersMapped() { return UniformBuffersMapped; }

private:
	// VkDevice	 Device;
	// VkRenderPass RenderPass;
	// VkQueue		 GraphicsQueue;
	FBufferConfig Config;

	std::vector<VkCommandBuffer> CommandBuffers;
	VkCommandPool				 CommandPool;

	VkBuffer	   VertexBuffer;
	VkDeviceMemory VertexBufferMemory;

	VkBuffer	   IndexBuffer;
	VkDeviceMemory IndexBufferMemory;

	std::vector<VkBuffer>		UniformBuffers;
	std::vector<VkDeviceMemory> UniformBuffersMemory;
	std::vector<void*>			UniformBuffersMapped;
};
