#pragma once

#include <Core/Common/NonNullPtr.h>
#include <Core/Math/Shape.h>
#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/D3D12GraphicsPipeline.h>
#include <Graphics/D3D12/D3D12Includes.h>
#include <Graphics/GraphicsDevice.h>

namespace Juliet::D3D12
{
    // Forward Declare
    struct D3D12Driver;
    struct D3D12Fence;
    struct D3D12Texture;
    struct D3D12TextureSubresource;
    struct D3D12WindowData;

    struct D3D12CommandListBaseData
    {
        ID3D12CommandAllocator* Allocator;
    };

    struct D3D12CopyCommandListData : D3D12CommandListBaseData
    {
        ID3D12GraphicsCommandList* CommandList;
    };

    struct D3D12GraphicsCommandListData : D3D12CommandListBaseData
    {
        ID3D12GraphicsCommandList6* CommandList;
    };

    struct D3D12PresentData
    {
        D3D12WindowData* WindowData;
        uint32           SwapChainImageIndex;
    };

    struct D3D12CommandList
    {
        CommandListHeader Common;

        uint64 ID;

        D3D12Driver* Driver;

        D3D12PresentData* PresentDatas;
        uint32            PresentDataCapacity;
        uint32            PresentDataCount;

        D3D12Fence* InFlightFence;
        bool        AutoReleaseFence;

        D3D12GraphicsCommandListData GraphicsCommandList;
        D3D12GraphicsCommandListData ComputeCommandList;
        D3D12CopyCommandListData     CopyCommandList;

        D3D12GraphicsPipeline* CurrentGraphicsPipeline;

        D3D12TextureSubresource* ColorTargetSubresources[GPUDriver::kMaxColorTargetInfo];
        D3D12TextureSubresource* ColorResolveSubresources[GPUDriver::kMaxColorTargetInfo];
        D3D12TextureSubresource* DepthStencilSubresource;

        bool NeedVertexBufferBind : 1;
        bool NeedVertexSamplerBind : 1;
        bool NeedVertexStorageTextureBind : 1;
        bool NeedVertexStorageBufferBind : 1;

        bool NeedFragmentSamplerBind : 1;
        bool NeedFragmentStorageTextureBind : 1;
        bool NeedFragmentStorageBufferBind : 1;

        bool NeedVertexUniformBufferBind[GPUDriver::kMaxUniformBuffersPerStage];
        bool NeedFragmentUniformBufferBind[GPUDriver::kMaxUniformBuffersPerStage];

        // D3D12UniformBuffer *vertexUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage];
        // D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage];

        Internal::D3D12DescriptorHeap* CRB_SRV_UAV_Heap;
        Internal::D3D12DescriptorHeap* Sampler_Heap;

        // Resource Tracking
        D3D12Texture** UsedTextures;
        uint32         UsedTextureCount;
        uint32         UsedTextureCapacity;

        D3D12GraphicsPipeline** UsedGraphicsPipelines;
        uint32                  UsedGraphicsPipelineCount;
        uint32                  UsedGraphicsPipelineCapacity;
    };

    extern CommandList* AcquireCommandList(NonNullPtr<GPUDriver> driver, QueueType queueType);
    extern bool         SubmitCommandLists(NonNullPtr<CommandList> commandList);
    extern void         SetViewPort(NonNullPtr<CommandList> commandList, const GraphicsViewPort& viewPort);
    extern void         SetScissorRect(NonNullPtr<CommandList> commandList, const Rectangle& rectangle);
    extern void         SetBlendConstants(NonNullPtr<CommandList> commandList, FColor blendConstants);
    extern void         SetBlendConstants(NonNullPtr<CommandList> commandList, FColor blendConstants);
    extern void         SetStencilReference(NonNullPtr<CommandList> commandList, uint8 reference);
    extern void         SetIndexBuffer(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer,
                                       IndexFormat format, size_t indexCount, index_t offset);
    extern void SetPushConstants(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex,
                                 uint32 numConstants, const void* constants);

    namespace Internal
    {
        extern void SetDescriptorHeaps(NonNullPtr<D3D12CommandList> commandList);

        extern void DestroyCommandList(NonNullPtr<D3D12CommandList> commandList);
        extern bool CleanCommandList(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12CommandList> commandList, bool cancel);

        extern void TrackGraphicsPipeline(NonNullPtr<D3D12CommandList> commandList, NonNullPtr<D3D12GraphicsPipeline> pipeline);
        extern void TrackTexture(NonNullPtr<D3D12CommandList> commandList, NonNullPtr<D3D12Texture> texture);
    } // namespace Internal
} // namespace Juliet::D3D12
