using System;
using X2Engine;


namespace UnityEngine.Rendering.Universal
{
    /// <summary>
    /// 
    /// Rendering Rain Occlusion Texture
    /// 
    /// </summary>
    internal class RainOcclusionPass : ScriptableRenderPass
    {
        const string m_ProfilerTag = "RainOcclusionPass";
        const int m_RainOccTexSize = 128;
        const float m_RainOccRange = 64.0f;

        // Snap Grid
        //const float m_TexSize = 0.5f;//m_RainOccRange / (float)m_RainOccTexSize;
        const float m_SnapGridSize = 2.0f;//m_texSize * 4;
        const float m_SnapGridHeight = 10.0f;
        Vector2Int m_GridOrigin = Vector2Int.zero;

        // RT
        RenderTargetHandle m_RainOcclusion;

        // Matrix
        Matrix4x4 m_ProjectionMatrix;
        Matrix4x4 m_ViewMatrix;
        Matrix4x4 m_WorldToOccMatrix;
        Plane[] m_FrustumPlanes = new Plane[6];

        static readonly int m_sp_worldToRainOcclusion = Shader.PropertyToID("_WorldToRainOcclusion");

        public RainOcclusionPass()
        {
            renderPassEvent = RenderPassEvent.BeforeRendering;
            m_RainOcclusion.Init("_RainOcclusionTexture");
            m_ProjectionMatrix = new Matrix4x4(
                new Vector4(2f / m_RainOccRange, 0f, 0f, 0f),
                new Vector4(0f, 2f / m_RainOccRange, 0f, 0f),
                new Vector4(0f, 0f, -2f/(200f-1f), 0f),
                new Vector4(0f, 0f, -(200f+1f)/(200f-1f), 1f)
                );
            //float halfOccRange = m_RainOccRange * 0.5f;
            //m_ProjectionMatrix = Matrix4x4.Ortho(-halfOccRange, halfOccRange, -halfOccRange, halfOccRange, 1f, 100f);
            //m_ProjectionMatrix = GL.GetGPUProjectionMatrix(m_ProjectionMatrix, true);
            m_ViewMatrix = Matrix4x4.identity;

        }

        ~RainOcclusionPass()
        {
            
        }

        public bool Setup(ref RenderingData renderingData)
        {
            //bool isRaining = EnvironmentSystem.instance && EnvironmentSystem.instance.IsRaining; ;
            //if (!isRaining)
            //    return false;

            Vector3 posCam = renderingData.cameraData.camera.transform.position;
            int xsnap = Mathf.CeilToInt(posCam.x / m_SnapGridSize);
            int ysnap = Mathf.CeilToInt(posCam.z / m_SnapGridSize);

            if (m_GridOrigin.x == xsnap && m_GridOrigin.y == ysnap)
                return false;

            m_GridOrigin.x = xsnap;
            m_GridOrigin.y = ysnap;
            posCam.x = (float)xsnap * m_SnapGridSize;
            posCam.z = (float)ysnap * m_SnapGridSize;

            Vector3 pos = posCam;
            pos.y += 100f;
            pos.y = Mathf.Ceil(pos.y / m_SnapGridHeight) * m_SnapGridHeight;

            m_ViewMatrix = Matrix4x4.TRS(new Vector3(-pos.x, -pos.z, -pos.y), Quaternion.Euler(-90, 0, 0), new Vector3(1, -1, 1));
            GeometryUtility.CalculateFrustumPlanes(m_ProjectionMatrix * m_ViewMatrix, m_FrustumPlanes);
            m_WorldToOccMatrix = GetOcclusionTransform(m_ProjectionMatrix, m_ViewMatrix);
            Shader.SetGlobalMatrix(m_sp_worldToRainOcclusion, m_WorldToOccMatrix);
            return true;
        }

        static Matrix4x4 GetOcclusionTransform(Matrix4x4 proj, Matrix4x4 view)
        {
            if (SystemInfo.usesReversedZBuffer)
            {
                proj.m20 = -proj.m20;
                proj.m21 = -proj.m21;
                proj.m22 = -proj.m22;
                proj.m23 = -proj.m23;
            }

            Matrix4x4 worldToShadow = proj * view;

            var textureScaleAndBias = Matrix4x4.identity;
            textureScaleAndBias.m00 = 0.5f;
            textureScaleAndBias.m11 = 0.5f;
            textureScaleAndBias.m22 = 0.5f;
            textureScaleAndBias.m03 = 0.5f;
            textureScaleAndBias.m23 = 0.5f;
            textureScaleAndBias.m13 = 0.5f;

            return textureScaleAndBias * worldToShadow;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            //m_RainOcclusionTexture = RenderTexture.GetTemporary(m_RainOccTexSize, m_RainOccTexSize, 16, RenderTextureFormat.Depth);
            //m_RainOcclusionTexture.filterMode = FilterMode.Bilinear;
            //m_RainOcclusionTexture.wrapMode = TextureWrapMode.Clamp;
            cmd.GetTemporaryRT(m_RainOcclusion.id, cameraTextureDescriptor, FilterMode.Bilinear);

            ConfigureTarget(m_RainOcclusion.Identifier(), m_RainOcclusion.Identifier());
            ConfigureClear(ClearFlag.All, Color.black);
            
           
        }

        /// <inheritdoc/>
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            Camera camera = renderingData.cameraData.camera;

            CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
            cmd.SetViewProjectionMatrices(m_ViewMatrix, m_ProjectionMatrix);
            cmd.SetGlobalVector("_ShadowBias", Vector3.zero);
            //var occRenderers = RainOcclusionReceiver.singleton.RainOccRenders;
            //var iter = occRenderers.GetEnumerator();
            //while (iter.MoveNext())
            //{
            //    var renderer = iter.Current.Value.renderer;
            //    var subMesh = iter.Current.Value.subMesh;
            //    if (null == renderer)
            //        continue;
            //    if (!GeometryUtility.TestPlanesAABB(m_FrustumPlanes, renderer.bounds))
            //        continue;
            //    var materials = renderer.sharedMaterials;
            //    if (subMesh == -1)
            //    {
            //        for (int i = 0; i < materials.Length; ++i)
            //        {
            //            var material = materials[i];
            //            cmd.DrawRenderer(renderer, material, i, material.FindPass("ShadowCaster"));
            //        }
            //    }
            //    else
            //    {
            //        if (subMesh >= 0 && subMesh < materials.Length)
            //        {
            //            var material = materials[subMesh];
            //            cmd.DrawRenderer(renderer, material, subMesh, material.FindPass("ShadowCaster"));
            //        }
            //    }
                
            //}

          
            cmd.SetGlobalTexture(m_RainOcclusion.id, m_RainOcclusion.Identifier());
            cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix);
            context.ExecuteCommandBuffer(cmd);
            
                
            CommandBufferPool.Release(cmd);
        }

        public override void FrameCleanup(CommandBuffer cmd)
        {
            if (cmd == null)
                throw new ArgumentNullException("cmd");
            cmd.ReleaseTemporaryRT(m_RainOcclusion.id);
        }

    }
}
