using UnityEngine;

namespace UnityEngine.Rendering.Universal
{
    // Motion vector data that persists over frames. (per camera)
    internal sealed class MotionVectorsPersistentData
    {
        #region Fields

        readonly Matrix4x4[] m_ViewProjection = new Matrix4x4[2];
        readonly Matrix4x4[] m_PreviousViewProjection = new Matrix4x4[2];
        readonly int[] m_LastFrameIndex = new int[2];
        readonly float[] m_PrevAspectRatio = new float[2];

        #endregion

        #region Constructors

        internal MotionVectorsPersistentData()
        {
            for (int i = 0; i < m_ViewProjection.Length; i++)
            {
                m_ViewProjection[i] = Matrix4x4.identity;
                m_PreviousViewProjection[i] = Matrix4x4.identity;
                m_LastFrameIndex[i] = -1;
                m_PrevAspectRatio[i] = -1;
            }
        }

        #endregion

        #region Properties

        internal int lastFrameIndex
        {
            get => m_LastFrameIndex[0];
        }

        internal Matrix4x4 viewProjection
        {
            get => m_ViewProjection[0];
        }

        internal Matrix4x4 previousViewProjection
        {
            get => m_PreviousViewProjection[0];
        }

        internal Matrix4x4[] viewProjectionStereo
        {
            get => m_ViewProjection;
        }

        internal Matrix4x4[] previousViewProjectionStereo
        {
            get => m_PreviousViewProjection;
        }
        #endregion

        internal int GetXRMultiPassId(ref CameraData cameraData)
        {
#if ENABLE_VR && ENABLE_XR_MODULE
            return cameraData.xr.enabled ? cameraData.xr.multipassId : 0;
#else
            return 0;
#endif
        }

        public void Update(ref CameraData cameraData)
        {
            var camera = cameraData.camera;
            int idx = GetXRMultiPassId(ref cameraData);

            // A camera could be rendered multiple times per frame, only update the view projections if needed
            bool aspectChanged = m_PrevAspectRatio[idx] != cameraData.aspectRatio;
            if (m_LastFrameIndex[idx] != Time.frameCount || aspectChanged)
            {
#if ENABLE_VR && ENABLE_XR_MODULE
                if (cameraData.xr.enabled && cameraData.xr.singlePassEnabled)
                {
                    var gpuVP0 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrix(0), true) * cameraData.GetViewMatrix(0);
                    var gpuVP1 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrix(1), true) * cameraData.GetViewMatrix(1);
                    m_PreviousViewProjection[0] = aspectChanged ? gpuVP0 : m_ViewProjection[0];
                    m_PreviousViewProjection[1] = aspectChanged ? gpuVP1 : m_ViewProjection[1];
                    m_ViewProjection[0] = gpuVP0;
                    m_ViewProjection[1] = gpuVP1;
                }
                else
#endif
                {
                    var gpuVP = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrix(0), true) * cameraData.GetViewMatrix(0);
                    m_PreviousViewProjection[idx] = aspectChanged ? gpuVP : m_ViewProjection[idx];
                    m_ViewProjection[idx] = gpuVP;
                }

                m_LastFrameIndex[idx] = Time.frameCount;
                m_PrevAspectRatio[idx] = cameraData.aspectRatio;
            }
        }
    }
}