/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 * For complete copyright and license terms please see the LICENSE at the root of this distribution.
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 *
 */
#pragma once

#include <RHI/Fence.h>
#include <RHI/CommandList.h>
#include <Atom/RHI/Scope.h>
#include <AzCore/Memory/PoolAllocator.h>

namespace AZ
{
    namespace DX12
    {
        class SwapChain;
        class Image;
        class Buffer;

        class Scope final
            : public RHI::Scope
        {
            using Base = RHI::Scope;
        public:
            AZ_RTTI(Scope, "{DE4C74AA-FD4C-4BC3-80DC-F405B72E4327}", Base);
            AZ_CLASS_ALLOCATOR(Scope, AZ::SystemAllocator, 0);

            static RHI::Ptr<Scope> Create();

            void Begin(
                CommandList& commandList,
                uint32_t commandListIndex,
                uint32_t commandListCount) const;

            void End(
                CommandList& commandList,
                uint32_t commandListIndex,
                uint32_t commandListCount) const;

            const bool IsStateSupportedByQueue(D3D12_RESOURCE_STATES state) const;

            void QueuePrologueTransition(const D3D12_RESOURCE_TRANSITION_BARRIER& transitionBarrier);
            void QueueEpilogueTransition(const D3D12_RESOURCE_TRANSITION_BARRIER& transitionBarrier);
            void QueueAliasingBarrier(const D3D12_RESOURCE_ALIASING_BARRIER& aliasingBarrier);
            void QueueResolveTransition(const D3D12_RESOURCE_TRANSITION_BARRIER& transitionBarrier);
            void QueuePreDiscardTransition(const D3D12_RESOURCE_TRANSITION_BARRIER& transitionBarrier);

            bool HasSignalFence() const;
            bool HasWaitFences() const;

            void SetSignalFenceValue(uint64_t fenceValue);
            uint64_t GetSignalFenceValue() const;

            void SetWaitFenceValueByQueue(RHI::HardwareQueueClass hardwareQueueClass, uint64_t fenceValue);
            uint64_t GetWaitFenceValueByQueue(RHI::HardwareQueueClass hardwareQueueClass) const;
            const FenceValueSet& GetWaitFences() const;

            // Returns true if the resource within the scopeAttachment is scheduled to be discarded
            bool IsResourceDiscarded(const RHI::ImageScopeAttachment& scopeAttachment) const;
            bool IsResourceDiscarded(const RHI::BufferScopeAttachment& scopeAttachment) const;
            
        private:
            Scope() = default;

            //////////////////////////////////////////////////////////////////////////
            // RHI::Scope
            void DeactivateInternal() override;
            void CompileInternal(RHI::Device& device) override;
            //////////////////////////////////////////////////////////////////////////

            void CompileAttachmentInternal(
                bool isFullResourceClear,
                const RHI::ScopeAttachment& binding,
                ID3D12Resource* resource);

            // Returns true if nativeResource is present within m_discardResourceRequests
            bool IsInDiscardResourceRequests(ID3D12Resource* nativeResource) const;
            
            /// A set of transition barriers for both before and after the scope.
            AZStd::vector<D3D12_RESOURCE_TRANSITION_BARRIER> m_prologueTransitionBarrierRequests;
            AZStd::vector<D3D12_RESOURCE_TRANSITION_BARRIER> m_epilogueTransitionBarrierRequests;
            AZStd::vector<D3D12_RESOURCE_TRANSITION_BARRIER> m_preDiscardTransitionBarrierRequests;

            /// A set of transition barriers for resolving a multisample image.
            AZStd::vector<D3D12_RESOURCE_TRANSITION_BARRIER> m_resolveTransitionBarrierRequests;

            /// Aliasing barrier requests for transient resources.
            AZStd::vector<D3D12_RESOURCE_ALIASING_BARRIER> m_aliasingBarriers;

            /// Array of color attachments, bound by index.
            AZStd::vector<const ImageView*> m_colorAttachments;

            /// [optional] Depth stencil attachment.
            const ImageView* m_depthStencilAttachment = nullptr;

            /// Depth stencil attachment access.
            RHI::ScopeAttachmentAccess m_depthStencilAccess = RHI::ScopeAttachmentAccess::ReadWrite;

            /// Clear image requests which use render target stage.
            AZStd::vector<CommandList::ImageClearRequest> m_clearRenderTargetRequests;

            /// Clear image requests which use unordered access.
            AZStd::vector<CommandList::ImageClearRequest> m_clearImageRequests;

            /// Clear buffer requests which use unordered access.
            AZStd::vector<CommandList::BufferClearRequest> m_clearBufferRequests;

            /// Discard resource requests for transient resources.
            AZStd::vector<ID3D12Resource*> m_discardResourceRequests;

            /// The list of fences to signal before executing this scope.
            FenceValueSet m_waitFencesByQueue = { {0} };

            /// The value to signal after executing this scope.
            uint64_t m_signalFenceValue = 0;
        };
    }
}
