// vim:fileencoding=utf-8:foldmethod=marker
//
// Prism RHI — Render Hardware Interface
//
// Design based on the How To Vulkan tutorial (https://www.howtovulkan.com/)
// which uses these modern Vulkan features:
//   - Vulkan 1.3 core (dynamic rendering, synchronization2, buffer device
//     address)
//   - Descriptor indexing (bindless with variable descriptor count)
//   - Dynamic state (viewport, scissor)
//   - VMA for memory management
//   - Vulkan profiles for device selection
//   - Slang for shader compilation (SPIR-V output)
//
// This RHI abstracts those features behind platform-agnostic types.
// Backend selection is compile-time — define PR_RHI_VULKAN, PR_RHI_D3D12,
// or PR_RHI_METAL at build time.
//
// Creation functions return the object directly or abort on failure.
// Only swapchain acquire/present return a b8 — the caller can detect
// out-of-date and recreate.

#ifndef PR_RHI_H
#define PR_RHI_H

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

#ifdef __cplusplus
extern "C" {
#endif

wp_extern PrRhiContext _G_RHI_CONTEXT;

wp_extern void prRhiInit(void);
wp_extern void prRhiDestroy(void);

// ======================================================================
// Instance
// ======================================================================

PrRhiInstance *prRhiCreateInstance(PrRhiInstanceDesc desc);
void           prRhiDestroyInstance(PrRhiInstance *inst);

// ======================================================================
// Physical device enumeration
// ======================================================================

PrRhiPhysicalDeviceArray      prRhiGetPhysicalDevices(PrRhiInstance *inst);
void                          prRhiGetPhysicalDeviceName(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void                          prRhiGetPhysicalDeviceDriverInfo(PrRhiPhysicalDevice *pdev, WpStr8 *out);
PrRhiPhysicalDeviceProperties prRhiGetPhysicalDeviceProperties(PrRhiPhysicalDevice *pdev);

// ======================================================================
// Surface (platform-specific)
// ======================================================================

PrRhiSurface            *prRhiCreateSurfaceFromWindow(PrRhiInstance *inst, SDL_Window *window);
void                     prRhiDestroySurface(PrRhiInstance *inst, PrRhiSurface *surface);
PrRhiSurfaceCapabilities prRhiGetSurfaceCapabilities(PrRhiPhysicalDevice *pdev,
                                                     PrRhiSurface *surface);

// ======================================================================
// Device
// ======================================================================

PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface,
                               PrRhiDeviceDesc desc);
void         prRhiDestroyDevice(PrRhiDevice *device);
void         prRhiDeviceWaitIdle(PrRhiDevice *device);
u32          prRhiGetQueueFamilyIndex(PrRhiDevice *device);

// ======================================================================
// Swapchain
// ======================================================================

PrRhiSwapchain      *prRhiCreateSwapchain(PrRhiDevice *device, PrRhiSwapchainDesc desc);
void                 prRhiDestroySwapchain(PrRhiDevice *device, PrRhiSwapchain *swapchain);

u32                  prRhiGetSwapchainImageCount(PrRhiSwapchain *swapchain);
PrRhiSwapchainResult prRhiAcquireNextImage(PrRhiDevice *device, PrRhiSwapchain *swapchain,
                                           PrRhiSemaphore *signal_semaphore, u32 *out_image_index);

PrRhiSwapchainResult prRhiPresent(PrRhiDevice *device, PrRhiSwapchain *swapchain,
                                  PrRhiSemaphore *wait_semaphore);

void                 prRhiRecreateSwapchain(PrRhiDevice *device, PrRhiSwapchain **swapchain,
                                             u32 width, u32 height);

PrRhiTexture        *prRhiGetSwapchainTexture(PrRhiSwapchain *swapchain, u32 image_index);
PrRhiTexture        *prRhiGetSwapchainDepthTexture(PrRhiSwapchain *swapchain);
PrRhiFormat          prRhiGetSwapchainFormat(PrRhiSwapchain *swapchain);

// ======================================================================
// Buffers
// ======================================================================

PrRhiBuffer       *prRhiCreateBuffer(PrRhiDevice *device, PrRhiBufferDesc desc);
void               prRhiDestroyBuffer(PrRhiDevice *device, PrRhiBuffer *buffer);

void              *prRhiBufferMap(PrRhiDevice *device, PrRhiBuffer *buffer);
void               prRhiBufferUnmap(PrRhiDevice *device, PrRhiBuffer *buffer);

PrRhiDeviceAddress prRhiGetBufferDeviceAddress(PrRhiDevice *device, PrRhiBuffer *buffer);

// ======================================================================
// Textures
// ======================================================================

PrRhiTexture *prRhiCreateTexture(PrRhiDevice *device, PrRhiTextureDesc desc);
PrRhiTexture *prRhiCreateTextureFromKtx(PrRhiDevice *device, const char *path,
                                          PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
void          prRhiDestroyTexture(PrRhiDevice *device, PrRhiTexture *texture);
PrRhiTextureSize prRhiGetTextureSize(PrRhiTexture *texture);

// ======================================================================
// Samplers
// ======================================================================

PrRhiSampler *prRhiCreateSampler(PrRhiDevice *device, PrRhiSamplerDesc desc);
void          prRhiDestroySampler(PrRhiDevice *device, PrRhiSampler *sampler);

// ======================================================================
// Shaders (from SPIR-V)
// ======================================================================

PrRhiShader *prRhiCreateShader(PrRhiDevice *device, PrRhiShaderDesc desc);
void         prRhiDestroyShader(PrRhiDevice *device, PrRhiShader *shader);

// ======================================================================
// Pipeline layouts
// ======================================================================

PrRhiPipelineLayout *prRhiCreatePipelineLayout(PrRhiDevice *device,
                                                PrRhiPipelineLayoutDesc desc);
void                 prRhiDestroyPipelineLayout(PrRhiDevice *device,
                                                 PrRhiPipelineLayout *layout);

// ======================================================================
// Pipelines
// ======================================================================

PrRhiPipeline *prRhiCreateGraphicsPipeline(PrRhiDevice *device,
                                            PrRhiGraphicsPipelineDesc desc);
PrRhiPipeline *prRhiCreateComputePipeline(PrRhiDevice *device,
                                           PrRhiComputePipelineDesc desc);
void           prRhiDestroyPipeline(PrRhiDevice *device, PrRhiPipeline *pipeline);

// ======================================================================
// Descriptor set layouts
// ======================================================================

PrRhiDescriptorSetLayout *prRhiCreateDescriptorSetLayout(PrRhiDevice *device,
                                                          PrRhiDescriptorSetLayoutDesc desc);
void                      prRhiDestroyDescriptorSetLayout(PrRhiDevice *device,
                                                           PrRhiDescriptorSetLayout *layout);

// ======================================================================
// Descriptor pools
// ======================================================================

PrRhiDescriptorPool *prRhiCreateDescriptorPool(PrRhiDevice *device,
                                                PrRhiDescriptorPoolDesc desc);
void                 prRhiDestroyDescriptorPool(PrRhiDevice *device,
                                                 PrRhiDescriptorPool *pool);
void                 prRhiResetDescriptorPool(PrRhiDevice *device,
                                               PrRhiDescriptorPool *pool);

// ======================================================================
// Descriptor sets
// ======================================================================

PrRhiDescriptorSet *prRhiAllocateDescriptorSet(PrRhiDevice *device, PrRhiDescriptorPool *pool,
                                               PrRhiDescriptorSetLayout *layout,
                                               WpU32Array variable_descriptor_counts);
void                prRhiFreeDescriptorSet(PrRhiDevice *device, PrRhiDescriptorPool *pool,
                                            PrRhiDescriptorSet *set);
void                prRhiUpdateDescriptorSet(PrRhiDevice *device, PrRhiWriteDescriptorSetArray writes);

// ======================================================================
// Fences and semaphores
// ======================================================================

PrRhiFence     *prRhiCreateFence(PrRhiDevice *device, PrRhiFenceDesc desc);
void            prRhiDestroyFence(PrRhiDevice *device, PrRhiFence *fence);

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

PrRhiSemaphore *prRhiCreateSemaphore(PrRhiDevice *device);
void            prRhiDestroySemaphore(PrRhiDevice *device, PrRhiSemaphore *semaphore);

// ======================================================================
// Command pools and command buffers
// ======================================================================

PrRhiCommandPool       *prRhiCreateCommandPool(PrRhiDevice *device);
void                    prRhiDestroyCommandPool(PrRhiDevice *device, PrRhiCommandPool *pool);

PrRhiCommandBufferArray prRhiAllocateCommandBuffers(PrRhiDevice *device, PrRhiCommandPool *pool,
                                                     u32 count);

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

// ======================================================================
// Command buffer recording
// ======================================================================

void prRhiBeginCommandBuffer(PrRhiCommandBuffer *cb);
void prRhiEndCommandBuffer(PrRhiCommandBuffer *cb);
void prRhiResetCommandBuffer(PrRhiCommandBuffer *cb);

// --- Pipeline barriers (synchronization2 style) ---

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

// --- Dynamic rendering ---

void prRhiCmdBeginRendering(PrRhiCommandBuffer *cb,
                            PrRhiColorAttachmentArray color_attachments,
                            const PrRhiDepthAttachment *depth_attachment);
void prRhiCmdEndRendering(PrRhiCommandBuffer *cb);

// --- Dynamic state ---

void prRhiCmdSetViewport(PrRhiCommandBuffer *cb, f32 x, f32 y, f32 width, f32 height);
void prRhiCmdSetScissor(PrRhiCommandBuffer *cb, i32 x, i32 y, u32 width, u32 height);

// --- Binding ---

void prRhiCmdBindPipeline(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint bind_point,
                          PrRhiPipeline *pipeline);
void prRhiCmdBindDescriptorSets(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint bind_point,
                                PrRhiPipelineLayout *layout, u32 first_set,
                                PrRhiDescriptorSetArray sets);
void prRhiCmdPushConstants(PrRhiCommandBuffer *cb, PrRhiPipelineLayout *layout,
                           PrRhiShaderStage stage_flags, u32 offset, u32 size,
                           const void *data);

// --- Vertex / index buffers ---

void prRhiCmdBindVertexBuffers(PrRhiCommandBuffer *cb, u32 first_binding, PrRhiBufferArray buffers,
                               WpU64Array offsets);
void prRhiCmdBindIndexBuffer(PrRhiCommandBuffer *cb, PrRhiBuffer *buffer, u64 offset,
                             PrRhiIndexType index_type);

// --- Draw calls ---

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

// --- Copy ---

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

// ======================================================================
// Queue submission
// ======================================================================

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

// ======================================================================
// Backend dispatch
// ======================================================================

#if defined(PR_RHI_VULKAN)
#  include "vulkan/pr_rhi_vk_aliases.h"
#elif defined(PR_RHI_D3D12)
#  error "D3D12 backend not yet implemented"
#elif defined(PR_RHI_METAL)
#  error "Metal backend not yet implemented"
#else
#  error "Define one of: PR_RHI_VULKAN, PR_RHI_D3D12, PR_RHI_METAL"
#endif

#ifdef __cplusplus
}
#endif

#endif

