// vim:fileencoding=utf-8:foldmethod=marker
//
// Vulkan backend declarations — concrete implementations of the RHI.
// Backend .c files include this header directly (never the umbrella pr_rhi.h)
// to avoid the #define aliases.

#ifndef PR_RHI_VK_H
#define PR_RHI_VK_H

#include "../pr_rhi_types.h"
#include <SDL3/SDL_video.h>
#include <volk/volk.h>
#include <vk_mem_alloc.h>

// ============================================================================
// Opaque struct definitions — visible only to the backend implementation.
// ============================================================================

#define PR_RHI_VK_PHYSICAL_DEVICE_NAME_LENGTH 256
#define PR_RHI_VK_PHYSICAL_DRIVER_INFO_LENGTH 256

struct PrRhiInstance {
	VkInstance                handle;           // VkInstance
	VkDebugUtilsMessengerEXT *debug_messenger;  // VkDebugUtilsMessengerEXT
};

struct PrRhiPhysicalDevice {
	VkPhysicalDevice  handle;
	PrRhiInstance    *instance;
	c8                device_name[PR_RHI_VK_PHYSICAL_DEVICE_NAME_LENGTH];
	c8                driver_info[PR_RHI_VK_PHYSICAL_DRIVER_INFO_LENGTH];
};

struct PrRhiDevice {
	VkDevice          handle;
	VkQueue           queue;
	u32               queue_family_index;
	VkPresentModeKHR  present_mode;
	VkPhysicalDevice  physical_device;
	VmaAllocator      allocator;
};

struct PrRhiSurface {
	VkSurfaceKHR handle;
};

struct PrRhiSwapchain {
	PrRhiDevice      *device;
	VkSwapchainKHR    handle;
	PrRhiSurface     *surface;
	u32               image_count;
	PrRhiTexture    **images;
	PrRhiTexture     *depth;
	VkFormat          format;
	VkFormat          depth_format;
	u32               width;
	u32               height;
	u32               current_image_index;
};

struct PrRhiBuffer {
	VkBuffer        handle;
	VmaAllocation   allocation;
	u64             device_address;
	u64             size;
	void           *mapped_data;
};

struct PrRhiTexture {
	VkImage         image;
	VkImageView     view;
	VmaAllocation   allocation;
	u32             width;
	u32             height;
	VkFormat        format;
};

struct PrRhiSampler {
	VkSampler handle;
};

struct PrRhiShader {
	VkShaderModule handle;
};

struct PrRhiPipelineLayout {
	VkPipelineLayout handle;
};

struct PrRhiPipeline {
	VkPipeline handle;
};

struct PrRhiDescriptorSetLayout {
	VkDescriptorSetLayout handle;
};

struct PrRhiDescriptorPool {
	VkDescriptorPool handle;
};

struct PrRhiDescriptorSet {
	VkDescriptorSet handle;
};

struct PrRhiCommandPool {
	VkCommandPool handle;
};

struct PrRhiCommandBuffer {
	VkCommandBuffer handle;
};

struct PrRhiFence {
	VkFence handle;
};

struct PrRhiSemaphore {
	VkSemaphore handle;
};

// ============================================================================
// Function declarations
// ============================================================================

PrRhiInstance *prRhiCreateInstanceVk(PrRhiInstanceDesc desc);
void           prRhiDestroyInstanceVk(PrRhiInstance *inst);

PrRhiPhysicalDeviceArray prRhiGetPhysicalDevicesVk(PrRhiInstance *inst);
void prRhiGetPhysicalDeviceNameVk(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void prRhiGetPhysicalDeviceDriverInfoVk(PrRhiPhysicalDevice *pdev, WpStr8 *out);
PrRhiPhysicalDeviceProperties prRhiGetPhysicalDevicePropertiesVk(PrRhiPhysicalDevice *pdev);

PrRhiSurface *prRhiCreateSurfaceFromWindowVk(PrRhiInstance *inst, SDL_Window *window);
void          prRhiDestroySurfaceVk(PrRhiInstance *inst, PrRhiSurface *surface);

PrRhiSurfaceCapabilities prRhiGetSurfaceCapabilitiesVk(PrRhiPhysicalDevice *pdev,
                                                        PrRhiSurface *surface);

PrRhiDevice *prRhiCreateDeviceVk(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface,
                                   PrRhiDeviceDesc desc);
void         prRhiDestroyDeviceVk(PrRhiDevice *device);
void         prRhiDeviceWaitIdleVk(PrRhiDevice *device);
u32          prRhiGetQueueFamilyIndexVk(PrRhiDevice *device);

PrRhiSwapchain *prRhiCreateSwapchainVk(PrRhiDevice *device, PrRhiSwapchainDesc desc);
void            prRhiDestroySwapchainVk(PrRhiDevice *device, PrRhiSwapchain *swapchain);
u32 prRhiGetSwapchainImageCountVk(PrRhiSwapchain *swapchain);
PrRhiSwapchainResult prRhiAcquireNextImageVk(PrRhiDevice *device, PrRhiSwapchain *swapchain,
                                              PrRhiSemaphore *signal_semaphore, u32 *out_image_index);
PrRhiSwapchainResult prRhiPresentVk(PrRhiDevice *device, PrRhiSwapchain *swapchain,
                                     PrRhiSemaphore *wait_semaphore);
void prRhiRecreateSwapchainVk(PrRhiDevice *device, PrRhiSwapchain **swapchain,
                               u32 width, u32 height);
PrRhiTexture *prRhiGetSwapchainTextureVk(PrRhiSwapchain *swapchain, u32 image_index);
PrRhiTexture *prRhiGetSwapchainDepthTextureVk(PrRhiSwapchain *swapchain);
PrRhiFormat   prRhiGetSwapchainFormatVk(PrRhiSwapchain *swapchain);

PrRhiBuffer *prRhiCreateBufferVk(PrRhiDevice *device, PrRhiBufferDesc desc);
void         prRhiDestroyBufferVk(PrRhiDevice *device, PrRhiBuffer *buffer);
void        *prRhiBufferMapVk(PrRhiDevice *device, PrRhiBuffer *buffer);
void         prRhiBufferUnmapVk(PrRhiDevice *device, PrRhiBuffer *buffer);
PrRhiDeviceAddress prRhiGetBufferDeviceAddressVk(PrRhiDevice *device, PrRhiBuffer *buffer);

PrRhiTexture *prRhiCreateTextureVk(PrRhiDevice *device, PrRhiTextureDesc desc);
PrRhiTexture *prRhiCreateTextureFromKtxVk(PrRhiDevice *device, const char *path,
                                            PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
void          prRhiDestroyTextureVk(PrRhiDevice *device, PrRhiTexture *texture);
PrRhiTextureSize prRhiGetTextureSizeVk(PrRhiTexture *texture);

PrRhiSampler *prRhiCreateSamplerVk(PrRhiDevice *device, PrRhiSamplerDesc desc);
void          prRhiDestroySamplerVk(PrRhiDevice *device, PrRhiSampler *sampler);

PrRhiShader *prRhiCreateShaderVk(PrRhiDevice *device, PrRhiShaderDesc desc);
void         prRhiDestroyShaderVk(PrRhiDevice *device, PrRhiShader *shader);

PrRhiPipelineLayout *prRhiCreatePipelineLayoutVk(PrRhiDevice *device,
                                                   PrRhiPipelineLayoutDesc desc);
void                 prRhiDestroyPipelineLayoutVk(PrRhiDevice *device,
                                                    PrRhiPipelineLayout *layout);

PrRhiPipeline *prRhiCreateGraphicsPipelineVk(PrRhiDevice *device,
                                               PrRhiGraphicsPipelineDesc desc);
PrRhiPipeline *prRhiCreateComputePipelineVk(PrRhiDevice *device,
                                              PrRhiComputePipelineDesc desc);
void           prRhiDestroyPipelineVk(PrRhiDevice *device, PrRhiPipeline *pipeline);

PrRhiDescriptorSetLayout *prRhiCreateDescriptorSetLayoutVk(PrRhiDevice *device,
                                                             PrRhiDescriptorSetLayoutDesc desc);
void                      prRhiDestroyDescriptorSetLayoutVk(PrRhiDevice *device,
                                                              PrRhiDescriptorSetLayout *layout);

PrRhiDescriptorPool *prRhiCreateDescriptorPoolVk(PrRhiDevice *device,
                                                   PrRhiDescriptorPoolDesc desc);
void                 prRhiDestroyDescriptorPoolVk(PrRhiDevice *device,
                                                    PrRhiDescriptorPool *pool);
void                 prRhiResetDescriptorPoolVk(PrRhiDevice *device,
                                                  PrRhiDescriptorPool *pool);

PrRhiDescriptorSet *prRhiAllocateDescriptorSetVk(PrRhiDevice *device,
                                                   PrRhiDescriptorPool *pool,
                                                   PrRhiDescriptorSetLayout *layout,
                                                   WpU32Array variable_descriptor_counts);
void                prRhiFreeDescriptorSetVk(PrRhiDevice *device, PrRhiDescriptorPool *pool,
                                               PrRhiDescriptorSet *set);
void prRhiUpdateDescriptorSetVk(PrRhiDevice *device, PrRhiWriteDescriptorSetArray writes);

PrRhiFence *prRhiCreateFenceVk(PrRhiDevice *device, PrRhiFenceDesc desc);
void        prRhiDestroyFenceVk(PrRhiDevice *device, PrRhiFence *fence);

void prRhiWaitForFencesVk(PrRhiDevice *device, PrRhiFenceArray fences, u32 count,
                           b8 wait_all, u64 timeout_ns);
void prRhiResetFencesVk(PrRhiDevice *device, PrRhiFenceArray fences, u32 count);

PrRhiSemaphore *prRhiCreateSemaphoreVk(PrRhiDevice *device);
void            prRhiDestroySemaphoreVk(PrRhiDevice *device, PrRhiSemaphore *semaphore);

PrRhiCommandPool *prRhiCreateCommandPoolVk(PrRhiDevice *device);
void              prRhiDestroyCommandPoolVk(PrRhiDevice *device, PrRhiCommandPool *pool);
PrRhiCommandBufferArray prRhiAllocateCommandBuffersVk(PrRhiDevice *device, PrRhiCommandPool *pool,
                                                        u32 count);

void prRhiFreeCommandBuffersVk(PrRhiDevice *device, PrRhiCommandPool *pool, PrRhiCommandBufferArray buffers);

void prRhiBeginCommandBufferVk(PrRhiCommandBuffer *cb);
void prRhiEndCommandBufferVk(PrRhiCommandBuffer *cb);
void prRhiResetCommandBufferVk(PrRhiCommandBuffer *cb);

void prRhiCmdPipelineBarrierVk(PrRhiCommandBuffer *cb,
                                PrRhiImageMemoryBarrierArray image_barriers,
                                PrRhiBufferMemoryBarrierArray buffer_barriers);

void prRhiCmdBeginRenderingVk(PrRhiCommandBuffer *cb,
                               PrRhiColorAttachmentArray color_attachments,
                               const PrRhiDepthAttachment *depth_attachment);
void prRhiCmdEndRenderingVk(PrRhiCommandBuffer *cb);

void prRhiCmdSetViewportVk(PrRhiCommandBuffer *cb, f32 x, f32 y, f32 width, f32 height);
void prRhiCmdSetScissorVk(PrRhiCommandBuffer *cb, i32 x, i32 y, u32 width, u32 height);

void prRhiCmdBindPipelineVk(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint bind_point,
                             PrRhiPipeline *pipeline);
void prRhiCmdBindDescriptorSetsVk(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint bind_point,
                                    PrRhiPipelineLayout *layout, u32 first_set,
                                    PrRhiDescriptorSetArray sets);
void prRhiCmdPushConstantsVk(PrRhiCommandBuffer *cb, PrRhiPipelineLayout *layout,
                              PrRhiShaderStage stage_flags, u32 offset, u32 size,
                              const void *data);
void prRhiCmdBindVertexBuffersVk(PrRhiCommandBuffer *cb, u32 first_binding, PrRhiBufferArray buffers,
                                 WpU64Array offsets);
void prRhiCmdBindIndexBufferVk(PrRhiCommandBuffer *cb, PrRhiBuffer *buffer, u64 offset,
                                PrRhiIndexType index_type);

void prRhiCmdDrawVk(PrRhiCommandBuffer *cb, u32 vertex_count, u32 instance_count,
                     u32 first_vertex, u32 first_instance);
void prRhiCmdDrawIndexedVk(PrRhiCommandBuffer *cb, u32 index_count, u32 instance_count,
                            u32 first_index, i32 vertex_offset, u32 first_instance);

void prRhiCmdCopyBufferToImageVk(PrRhiCommandBuffer *cb, PrRhiBuffer *src, PrRhiTexture *dst,
                                  PrRhiBufferImageCopyArray copies);

void prRhiQueueSubmitVk(PrRhiDevice *device, PrRhiCommandBuffer *cb,
                         PrRhiSemaphore *wait_semaphore,
                         PrRhiSemaphore *signal_semaphore, PrRhiFence *fence);

#endif
