#pragma once

#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/D3D12Includes.h>
#include <Graphics/GraphicsDevice.h>

struct ID3D12Resource;

namespace Juliet::D3D12
{
    // Forward Declare
    struct D3D12Texture;
    struct D3D12ResourceHeap;
    struct D3D12CommandList;

    struct D3D12TextureContainer
    {
        TextureHeader Header;

        D3D12Texture*  ActiveTexture;
        D3D12Texture** Textures;
        uint32         Capacity;
        uint32         Count;

        // Note: Swapchain images cannot be cycled
        bool CanBeCycled;

#if JULIET_DEBUG
        char* DebugName;
#endif
    };

    // D3D12 subresourcces: https://learn.microsoft.com/en-us/windows/win32/direct3d12/subresources (mipmaps, etc..)
    struct D3D12TextureSubresource
    {
        D3D12Texture* Parent;
        uint32        Layer;
        uint32        Level;
        uint32        Depth;
        uint32        Index;

        // One per depth slice
        D3D12StagingDescriptor* RTVHandles; // NULL if not a color target

        D3D12StagingDescriptor UAVHandle; // NULL if not a compute storage write texture
        D3D12StagingDescriptor DSVHandle; // NULL if not a depth stencil target
    };

    struct D3D12Texture
    {
        D3D12TextureContainer* Container;
        uint32                 IndexInContainer;

        ID3D12Resource* Resource;

        D3D12TextureSubresource* Subresources;
        uint32                   SubresourceCount; // Layer Count * number of Levels

        D3D12StagingDescriptor SRVHandle;

        // TODO: Should be atomic to support multithreading
        int32 ReferenceCount;
    };

    namespace Internal
    {
        extern D3D12TextureSubresource* PrepareTextureSubresourceForWrite(NonNullPtr<D3D12CommandList>,
                                                                          NonNullPtr<D3D12TextureContainer> container,
                                                                          uint32 layer, uint32 level, bool shouldCycle,
                                                                          D3D12_RESOURCE_STATES newTextureUsage);
        extern D3D12TextureSubresource* FetchTextureSubresource(NonNullPtr<D3D12TextureContainer> container,
                                                                uint32 layer, uint32 level);
        extern void                     TextureSubresourceBarrier(NonNullPtr<D3D12CommandList> commandList,
                                                                  D3D12_RESOURCE_STATES sourceState, D3D12_RESOURCE_STATES destinationState,
                                                                  NonNullPtr<D3D12TextureSubresource> textureSubresource);

        // Texture usage transition
        extern void TextureSubresourceTransitionFromDefaultUsage(NonNullPtr<D3D12CommandList>        commandList,
                                                                 NonNullPtr<D3D12TextureSubresource> subresource,
                                                                 D3D12_RESOURCE_STATES               toTextureUsage);
        extern void TextureTransitionFromDefaultUsage(NonNullPtr<D3D12CommandList> commandList,
                                                      NonNullPtr<D3D12Texture> texture, D3D12_RESOURCE_STATES toTextureUsage);
        extern void TextureSubresourceTransitionToDefaultUsage(NonNullPtr<D3D12CommandList>        commandList,
                                                               NonNullPtr<D3D12TextureSubresource> subresource,
                                                               D3D12_RESOURCE_STATES               fromTextureUsage);
        extern void TextureTransitionToDefaultUsage(NonNullPtr<D3D12CommandList> commandList, NonNullPtr<D3D12Texture> texture,
                                                    D3D12_RESOURCE_STATES fromTextureUsage);

        // Utils
        extern DXGI_FORMAT ConvertToD3D12TextureFormat(TextureFormat format);
        extern DXGI_FORMAT ConvertToD3D12DepthFormat(TextureFormat format);
        extern uint32      JulietToD3D12_SampleCount[];
    } // namespace Internal

    extern Texture* CreateTexture(NonNullPtr<GPUDriver> driver, const TextureCreateInfo& createInfo);
    extern void     DestroyTexture(NonNullPtr<GPUDriver> driver, NonNullPtr<Texture> texture);
} // namespace Juliet::D3D12
