#if !defined(LD_VULKAN_CORE_H_)
#define LD_VULKAN_CORE_H_

#if OS_WINDOWS
	#define VK_USE_PLATFORM_WIN32_KHR 1

	#if defined(CreateSemaphore)
		// Sigh
		#undef CreateSemaphore
	#endif
#elif OS_LINUX
	#define VK_USE_PLATFORM_XLIB_KHR    1
	#define VK_USE_PLATFORM_WAYLAND_KHR 1
#endif

#if defined(function)
	#undef function
#endif

#define VK_NO_PROTOTYPES 1
#include <vulkan/vulkan.h>

#define function static

#define VK_MAX_FRAMES_IN_FLIGHT 8
#define VK_NUM_SCRATCH 8

typedef struct Vk_Image Vk_Image;
struct Vk_Image {
	VkDeviceMemory memory;
	VkDeviceSize   size;

	VkImage handle;
	VkImageView view;

	VkFormat format;
	VkImageUsageFlags usage;
	U32 width, height;
};

typedef struct Vk_Buffer Vk_Buffer;
struct Vk_Buffer {
	VkBuffer handle;

	VkDeviceMemory memory;
	VkDeviceSize   size;

	VkBufferUsageFlags usage;

	B32 host_visible;
	void *data; // if mapped host visible memory
};

typedef struct Vk_Shader Vk_Shader;
struct Vk_Shader {
	VkShaderModule handle;
	VkShaderStageFlags stage;
};

typedef struct Vk_Pipeline Vk_Pipeline;
struct Vk_Pipeline {
	VkPipeline handle;

	struct {
		VkPipelineLayout pipeline;
		VkDescriptorSetLayout set;
	} layout;

	struct {
		VkFormat items[8];
		U32 count;
	} targets;

	struct {
		Vk_Shader items[8];
		U32 count;
	} shaders;
};

typedef struct Vk_CommandBuffer Vk_CommandBuffer;
struct Vk_CommandBuffer {
	VkCommandBuffer handle;
	VkFence fence;
};

typedef struct Vk_Frame Vk_Frame;
struct Vk_Frame {
	VkCommandPool   pool;
	VkCommandBuffer cmd;

	VkDescriptorPool descriptors;

	VkSemaphore acquire, complete;
	VkFence fence;

	U32 next_scratch;
	Vk_CommandBuffer scratch[VK_NUM_SCRATCH];

	Vk_Buffer rbo;

	U32 image; // swapchain image index
};

typedef struct Vk_Context Vk_Context;
struct Vk_Context {
	void *lib;
	PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
	PFN_vkCreateInstance      CreateInstance;

	#define VK_INSTANCE_FUNCTIONS
	#define VK_DEVICE_FUNCTIONS
	#define VK_FUNC(x) PFN_vk##x x
		#include "functions.h"
	#undef VK_FUNC

	VkResult   err;
	VkInstance instance;

	VkSurfaceKHR surface;

	VkPhysicalDevice gpu;
	VkDevice device;

	U32 n_frames;  // monotonically increasing
	U32 in_flight; // number of frames in flight, number of swapchain images + 1

	Vk_Frame frames[VK_MAX_FRAMES_IN_FLIGHT];

	struct {
		U32 family;
		VkQueue handle;
	} queue;

	struct {
		VkSwapchainKHR handle;

		U32 n_images;
		VkImage     images[8];
		VkImageView views[8];

		VkSurfaceFormatKHR format;

		U32 width;
		U32 height;
	} swapchain;

	VkSampler sampler;
};

extern Vk_Context vk;

function bool Vk_Setup(SDL_Window *window);

function Vk_Frame *Vk_FrameBegin(SDL_Window *window);
function void Vk_FrameEnd();

function Vk_CommandBuffer *Vk_CommandBufferPush();
function void              Vk_CommandBufferSubmit(Vk_CommandBuffer *cmds, B32 wait);

function void Vk_BufferCreate(Vk_Buffer *buffer);
function void Vk_ImageCreate(Vk_Image *image);

#endif  // LD_VULKAN_CORE_H_
