#ifndef RT_VK_BINDLESS_REGISTRY_H
#define RT_VK_BINDLESS_REGISTRY_H

#include <runtime/runtime.h>
#include <runtime/threading.h>
#include <stdbool.h>
#include <volk/volk.h>

struct rt_vk_device;

enum { RT_VK_INVALID_BINDLESS_HANDLE_VALUE = UINT32_MAX };

typedef enum {
    RT_VK_BINDLESS_HANDLE_TYPE_UNIFORM_BUFFER,
    RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_BUFFER,
    RT_VK_BINDLESS_HANDLE_TYPE_SAMPLED_IMAGE,
    RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_IMAGE,
    RT_VK_BINDLESS_HANDLE_TYPE_SAMPLER,
} rt_vk_bindless_handle_type;

/* Handle to a bindless resource.
 * The layout is:
 * | type : 3 | index : 29 |
 * MSB                   LSB
 */
typedef struct {
    uint32_t value;
} rt_vk_bindless_handle;

/* Utilities for bindless handles */

static RT_INLINE bool rtIsVkBindlessHandleValid(rt_vk_bindless_handle handle) {
    return handle.value != RT_VK_INVALID_BINDLESS_HANDLE_VALUE;
}

static RT_INLINE rt_vk_bindless_handle_type rtGetVkBindlessHandleType(rt_vk_bindless_handle handle) {
    return (handle.value >> 29) & 0x7;
}

static RT_INLINE uint32_t rtGetVkBindlessHandleIndex(rt_vk_bindless_handle handle) {
    return handle.value & ((1u << 29) - 1);
}

typedef struct {
    uint32_t *indices;
    uint32_t size;
} rt_vk_bindless_reuse_stack;

typedef struct {
    rt_vk_bindless_handle handle;
    /* Frame id on which the handle should be released */
    uint32_t frame;
} rt_vk_bindless_release_list_entry;

/* Bindless registry manages the global descriptor set of bindless resources. */
typedef struct {
    struct rt_vk_device *dev;
    rt_mutex *mutex;

    VkDescriptorSetLayout bindless_set_layout;
    VkDescriptorPool bindless_set_pool;
    VkDescriptorSet bindless_set;

    uint32_t uniform_buffer_binding;
    uint32_t storage_buffer_binding;
    uint32_t sampled_image_binding;
    uint32_t storage_image_binding;
    uint32_t sampler_binding;

    rt_vk_bindless_reuse_stack uniform_buffer_reuse_stack;
    rt_vk_bindless_reuse_stack storage_buffer_reuse_stack;
    rt_vk_bindless_reuse_stack sampled_image_reuse_stack;
    rt_vk_bindless_reuse_stack storage_image_reuse_stack;
    rt_vk_bindless_reuse_stack sampler_reuse_stack;
    uint32_t next_uniform_buffer;
    uint32_t next_storage_buffer;
    uint32_t next_sampled_image;
    uint32_t next_storage_image;
    uint32_t next_sampler;

    rt_vk_bindless_release_list_entry *release_list;
    uint32_t release_list_length;
} rt_vk_bindless_registry;

typedef struct {
    rt_result result;
    rt_vk_bindless_registry bindless_registry;
} rt_create_vk_bindless_registry_result;

rt_create_vk_bindless_registry_result rtCreateVkBindlessRegistry(struct rt_vk_device *dev);

void rtDestroyVkBindlessRegistry(rt_vk_bindless_registry *registry);

void rtVkBindlessRegistryOnBeginFrame(rt_vk_bindless_registry *registry);

rt_vk_bindless_handle rtStoreUniformBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer);
rt_vk_bindless_handle rtStoreStorageBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer);
rt_vk_bindless_handle rtStoreSampledImage(rt_vk_bindless_registry *registry, VkImageView image);
rt_vk_bindless_handle rtStoreStorageImage(rt_vk_bindless_registry *registry, VkImageView image);
rt_vk_bindless_handle rtStoreSampler(rt_vk_bindless_registry *registry, VkSampler sampler);

void rtFreeBindlessResource(rt_vk_bindless_registry *registry, rt_vk_bindless_handle handle);


#endif
