#pragma once

#include <Core/Common/NonNullPtr.h>
#include <Graphics/D3D12/D3D12Includes.h>
#include <Graphics/D3D12/D3D12Shader.h>
#include <Graphics/GraphicsDevice.h>
#include <Graphics/GraphicsPipeline.h>

namespace Juliet
{
    struct GraphicsPipelineCreateInfo;
    struct GPUDriver;
    struct GraphicsPipeline;
} // namespace Juliet

namespace Juliet::D3D12
{
    struct D3D12Shader;

    struct D3D12GraphicsRootSignature
    {
        ID3D12RootSignature* Handle;
    };

    struct D3D12GraphicsPipeline
    {
#if ALLOW_SHADER_HOT_RELOAD
        // Template to recreate an ID3D12PipelineState when shader are hot reloaded
        // Stripped out in shipping build as the struct is huge
        D3D12_GRAPHICS_PIPELINE_STATE_DESC PSODescTemplate;

        // Keeping shaders byte code to make it easier to recreate the ID3D12PipelineState
        // Those will be freed when the pipeline is destroyed or updated
        D3D12Shader* VertexShaderCache;
        D3D12Shader* FragmentShaderCache;
#endif

        ID3D12PipelineState*        PipelineState;
        D3D12GraphicsRootSignature* RootSignature;
        PrimitiveType               PrimitiveType;

        uint32 VertexStrides[GPUDriver::kMaxVertexBuffers];

        uint32 VertexSamplerCount;
        uint32 VertexUniformBufferCount;
        uint32 VertexStorageBufferCount;
        uint32 VertexStorageTextureCount;

        uint32 FragmentSamplerCount;
        uint32 FragmentUniformBufferCount;
        uint32 FragmentStorageBufferCount;
        uint32 FragmentStorageTextureCount;

        int ReferenceCount;
    };

    extern GraphicsPipeline* CreateGraphicsPipeline(NonNullPtr<GPUDriver> driver, const GraphicsPipelineCreateInfo& createInfo);
    extern void DestroyGraphicsPipeline(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsPipeline> graphicsPipeline);
    extern bool UpdateGraphicsPipelineShaders(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsPipeline> graphicsPipeline,
                                              Shader* optional_vertexShader, Shader* optional_fragmentShader);
    namespace Internal
    {
        extern void ReleaseGraphicsPipeline(NonNullPtr<D3D12GraphicsPipeline> d3d12GraphicsPipeline);
    }
} // namespace Juliet::D3D12
