using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR;

public class DepthRenderPassFeature : ScriptableRendererFeature
{
    class DepthRenderPass : ScriptableRenderPass
    {
        public static Action<float[]> OnDepthReady;
        public static Queue<bool> Requests;
        public static Queue<float[]> Answers;

        ProfilingSampler m_ProfilingSampler = new ProfilingSampler("ColorBlit");
        private Material material;
        private RTHandle rtHandle;
        private bool depth = false;
        private ComputeBuffer computeBuffer;

        public DepthRenderPass(Material material)
        {
            this.material = material;

            Requests = new Queue<bool>();
            Answers = new Queue<float[]>();
        }

        public void SetTarget(RTHandle handle, bool depth)
        {
            rtHandle = handle;
            this.depth = depth;

            if (computeBuffer != null) DestroyComputeBuffer();
                
            RenderTexture rt = rtHandle.rt;
            computeBuffer = new ComputeBuffer(rt.width * rt.height, 1 * sizeof(float));
            if(material != null)
            {
                material.SetBuffer("distBuffer", computeBuffer);
                material.SetFloat("res", rt.height);
                material.SetInt("_RenderDepth", this.depth ? 1 : 0);
            }
        }

        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            ConfigureTarget(rtHandle);
        }

        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get();
            //RTHandle rt = renderingData.cameraData.renderer.cameraColorTargetHandle;

            using (new ProfilingScope(cmd, m_ProfilingSampler))
            {
                Blitter.BlitCameraTexture(cmd, rtHandle, rtHandle, material, 0);
                if(Requests.Count > 0)
                {
                    Requests.Dequeue();
                    float[] resultArray = new float[computeBuffer.count];
                    computeBuffer.GetData(resultArray);
                    Answers.Enqueue(resultArray);
                }
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        // Cleanup any allocated resources that were created during the execution of this render pass.
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
        }

        public void Dispose()
        {
#if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Destroy(material);
            }
            else
            {
                DestroyImmediate(material);
            }
#else
            Destroy(material);
#endif
            if (rtHandle != null) rtHandle.Release();

            if (computeBuffer != null)
            {
                DestroyComputeBuffer();
            }

            if (Requests != null)
            {
                Requests.Clear();
                Requests = null;
            }

            if (Answers != null)
            {
                Answers.Clear();
                Answers = null;
            }
        }

        public void DestroyComputeBuffer()
        {
            computeBuffer.Release();
            computeBuffer = null;
        }
    }

    [SerializeField] Shader shader;
    [SerializeField] bool Depth = false;
    DepthRenderPass m_ScriptablePass;
    Material material;

    /// <inheritdoc/>
    public override void Create()
    {
        if(shader == null) shader = TexturesSettings.Instance.DepthShader;
        material = CoreUtils.CreateEngineMaterial(shader);
        m_ScriptablePass = new DepthRenderPass(material);

        // Configures where the render pass should be injected.
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    }

    // Here you can inject one or multiple render passes in the renderer.
    // This method is called when setting up the renderer once per-camera.
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.camera.tag == "Eye")
        {
            renderer.EnqueuePass(m_ScriptablePass);
        }
    }

    public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    {
        m_ScriptablePass.ConfigureInput(ScriptableRenderPassInput.Color);
        m_ScriptablePass.SetTarget(renderer.cameraColorTargetHandle, Depth);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
#if UNITY_EDITOR
        if (EditorApplication.isPlaying)
        {
            Destroy(material);
        }
        else
        {
            DestroyImmediate(material);
        }
#else
            Destroy(material);
#endif
        m_ScriptablePass.Dispose(); 
    }

}


