using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Experimental.Rendering;

namespace UnityEngine.Rendering.Universal.Internal
{
    internal sealed class RenderTargetBufferSystem
    {
        struct SwapBuffer
        {
            public RTHandle rtMSAA;
            public RTHandle rtResolve;
            public string name;
            public int msaa;
        }
        SwapBuffer m_A, m_B;
        static bool m_AisBackBuffer = true;

        static RenderTextureDescriptor m_Desc;
        FilterMode m_FilterMode;
        bool m_AllowMSAA = true;

        ref SwapBuffer backBuffer { get { return ref m_AisBackBuffer ? ref m_A : ref m_B; } }
        ref SwapBuffer frontBuffer { get { return ref m_AisBackBuffer ? ref m_B : ref m_A; } }

        public RenderTargetBufferSystem(string name)
        {
            m_A.name = name + "A";
            m_B.name = name + "B";
        }

        public void Dispose()
        {
            m_A.rtMSAA?.Release();
            m_B.rtMSAA?.Release();
            m_A.rtResolve?.Release();
            m_B.rtResolve?.Release();
        }

        public RTHandle PeekBackBuffer()
        {
            return (m_AllowMSAA && backBuffer.msaa > 1) ? backBuffer.rtMSAA : backBuffer.rtResolve;
        }

        public RTHandle GetBackBuffer(CommandBuffer cmd)
        {
            ReAllocate(cmd);
            return PeekBackBuffer();
        }

        public RTHandle GetFrontBuffer(CommandBuffer cmd)
        {
            if (!m_AllowMSAA && frontBuffer.msaa > 1)
                frontBuffer.msaa = 1;

            ReAllocate(cmd);

            return (m_AllowMSAA && frontBuffer.msaa > 1) ? frontBuffer.rtMSAA : frontBuffer.rtResolve;
        }

        public void Swap()
        {
            m_AisBackBuffer = !m_AisBackBuffer;
        }

        void ReAllocate(CommandBuffer cmd)
        {
            var desc = m_Desc;

            desc.msaaSamples = m_A.msaa;
            if (desc.msaaSamples > 1)
                RenderingUtils.ReAllocateHandleIfNeeded(ref m_A.rtMSAA, desc, m_FilterMode, TextureWrapMode.Clamp, name: m_A.name);

            desc.msaaSamples = m_B.msaa;
            if (desc.msaaSamples > 1)
                RenderingUtils.ReAllocateHandleIfNeeded(ref m_B.rtMSAA, desc, m_FilterMode, TextureWrapMode.Clamp, name: m_B.name);

            desc.msaaSamples = 1;
            RenderingUtils.ReAllocateHandleIfNeeded(ref m_A.rtResolve, desc, m_FilterMode, TextureWrapMode.Clamp, name: m_A.name);
            RenderingUtils.ReAllocateHandleIfNeeded(ref m_B.rtResolve, desc, m_FilterMode, TextureWrapMode.Clamp, name: m_B.name);
            cmd.SetGlobalTexture(m_A.name, m_A.rtResolve);
            cmd.SetGlobalTexture(m_B.name, m_B.rtResolve);
        }

        public void Clear()
        {
            m_AisBackBuffer = true;
            m_AllowMSAA = m_A.msaa > 1 || m_B.msaa > 1;
        }

        public void SetCameraSettings(RenderTextureDescriptor desc, FilterMode filterMode)
        {
            desc.depthStencilFormat = GraphicsFormat.None;
            m_Desc = desc;
            m_FilterMode = filterMode;

            m_A.msaa = m_Desc.msaaSamples;
            m_B.msaa = m_Desc.msaaSamples;

            if (m_Desc.msaaSamples > 1)
                EnableMSAA(true);
        }

        public RTHandle GetBufferA()
        {
            return (m_AllowMSAA && m_A.msaa > 1) ? m_A.rtMSAA : m_A.rtResolve;
        }

        public void EnableMSAA(bool enable)
        {
            m_AllowMSAA = enable;
            if (enable)
            {
                m_A.msaa = m_Desc.msaaSamples;
                m_B.msaa = m_Desc.msaaSamples;
            }
        }
    }
}