#pragma once

#include <Core/Common/NonNullPtr.h>
#include <Core/Networking/NetworkPacket.h>
#include <Graphics/D3D12/D3D12Includes.h>

// Forward declare
namespace Juliet::D3D12
{
    struct D3D12Driver;
}

namespace Juliet::D3D12::Internal
{
    // https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptor-heaps
    struct D3D12DescriptorHeap
    {
        D3D12DescriptorHeap*        Next;
        ID3D12DescriptorHeap*       Handle;
        D3D12_DESCRIPTOR_HEAP_TYPE  HeapType;
        D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapCPUStart;
        D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeapGPUStart; // only used by GPU heaps
        uint32                      MaxDescriptors;
        uint32                      DescriptorSize;
        uint32                      CurrentDescriptorIndex; // only used by GPU heaps

        VectorArena<uint32> FreeIndices;
        index_t             CurrentFreeIndex;

        bool Staging : 1;
    };

    struct D3D12Descriptor
    {
        D3D12DescriptorHeap*        Heap;
        uint32                      Index;
        D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle;
        D3D12_GPU_DESCRIPTOR_HANDLE GpuHandle;
    };

    struct D3D12DescriptorHeapPool
    {
        Arena*               Arena;
        D3D12DescriptorHeap* FirstFreeDescriptorHeap;
    };

    using DescriptorHeapCreator = D3D12DescriptorHeap* (*)(NonNullPtr<D3D12Driver>, D3D12DescriptorHeapPool& heapPool,
                                                           D3D12_DESCRIPTOR_HEAP_TYPE, uint32, bool);

    extern void CreateDescriptorHeapPool(NonNullPtr<D3D12Driver> driver, D3D12DescriptorHeapPool& heapPool,
                                         D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count);
    extern void DestroyDescriptorHeapPool(D3D12DescriptorHeapPool& pool);

    extern D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, NonNullPtr<Arena> arena,
                                                     D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging);
    extern void                 DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap);

    extern D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver);
    extern void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, NonNullPtr<D3D12DescriptorHeap> heap);

    extern bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor);
    extern void ReleaseDescriptor(const D3D12Descriptor& descriptor);
} // namespace Juliet::D3D12::Internal
