#pragma once

#include <Core/Common/EnumUtils.h>
#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
#include <Graphics/D3D12/D3D12GraphicsPipeline.h>
#include <Graphics/D3D12/D3D12Texture.h>
#include <Graphics/GraphicsDevice.h>

namespace Juliet
{
    struct DynamicLibrary;
}

namespace Juliet::D3D12
{
    struct D3D12StagingDescriptorPool;
    // Forward Declare
    struct D3D12CommandList;
    struct D3D12Fence;

    enum class RootParameters : uint8
    {
        Constants32Bits,
        Count,
    };

    struct D3D12WindowData
    {
        Window* Window;

        IDXGISwapChain3*      SwapChain;
        D3D12TextureContainer SwapChainTextureContainers[GPUDriver::kMaxFramesInFlight];
        DXGI_COLOR_SPACE_TYPE SwapChainColorSpace;
        SwapChainComposition  SwapChainComposition;
        uint8                 SwapChainTextureCount;

        PresentMode PresentMode;

        Fence* InFlightFences[GPUDriver::kMaxFramesInFlight];

        uint32 WindowFrameCounter; // Specific to that window. See GraphicsDevice for global counter
        uint32 Width;
        uint32 Height;
    };

    struct D3D12Driver : GPUDriver
    {
        GraphicsDevice* GraphicsDevice;

        // D3D12
        DynamicLibrary*                              D3D12DLL;
        ID3D12Device5*                               D3D12Device;
        PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE D3D12SerializeVersionedRootSignatureFct;
        ID3D12CommandQueue*                          GraphicsQueue;
        D3D12_COMMAND_QUEUE_DESC                     QueueDesc[ToUnderlying(QueueType::Count)];
#if JULIET_DEBUG
        ID3D12Debug1* D3D12Debug;
#endif

        // Indirect commands signature
        ID3D12CommandSignature* IndirectDrawCommandSignature;
        ID3D12CommandSignature* IndirectIndexedDrawCommandSignature;
        ID3D12CommandSignature* IndirectDispatchCommandSignature;

        // DXGI
        IDXGIFactory4*  DXGIFactory;
        IDXGIAdapter1*  DXGIAdapter;
#if JULIET_DEBUG
        DynamicLibrary* DXGIDebugDLL;
        IDXGIDebug*     DXGIDebug;
#ifdef IDXGIINFOQUEUE_SUPPORTED
        IDXGIInfoQueue* DXGIInfoQueue;
#endif
#endif

        // Windows
        // TODO: Support more than one window
        D3D12WindowData* WindowData;

        // Resources
        D3D12CommandList** AvailableCommandLists;
        uint8              AvailableCommandListCapacity;
        uint8              AvailableCommandListCount;

        D3D12CommandList** SubmittedCommandLists;
        uint8              SubmittedCommandListCapacity;
        uint8              SubmittedCommandListCount;

        D3D12Fence** AvailableFences;
        uint32       AvailableFenceCount;
        uint32       AvailableFenceCapacity;

        D3D12GraphicsPipeline** GraphicsPipelinesToDispose;
        uint32                  GraphicsPipelinesToDisposeCount;
        uint32                  GraphicsPipelinesToDisposeCapacity;

        D3D12GraphicsRootSignature* BindlessRootSignature;

        D3D12StagingDescriptorPool*       StagingDescriptorPools[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
        Internal::D3D12DescriptorHeap*    BindlessDescriptorHeap;
        Internal::D3D12DescriptorHeapPool SamplerHeapPool;

        String Semantic;

        uint8  FramesInFlight;
        uint64 FrameCounter = 0; // Number of frame since inception

        bool IsTearingSupported : 1;
        bool IsUMAAvailable : 1;
        bool IsUMACacheCoherent : 1;
        bool GPUUploadHeapSupported : 1;
    };


    namespace Internal
    {
        void DisposePendingResourcces(NonNullPtr<D3D12Driver> driver);
    }
} // namespace Juliet::D3D12
