using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
/**
 * https://www.febucci.com/2022/05/custom-post-processing-in-urp/
 * 
 */

namespace Shaders
{
    [System.Serializable]
    public class CustomPostProcessPass : ScriptableRenderPass
    {
        // Used to render from camera to post processings
        // back and forth, until we render the final image to
        // the camera
        RenderTargetIdentifier source;
        RenderTargetIdentifier destinationA;
        RenderTargetIdentifier destinationB;
        RenderTargetIdentifier latestDest;

        readonly int temporaryRTIdA = Shader.PropertyToID("_TempRT");
        readonly int temporaryRTIdB = Shader.PropertyToID("_TempRTB");

        public CustomPostProcessPass()
        {
            // Set the render pass event
            renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        }

        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            // Grab the camera target descriptor. We will use this when creating a temporary render texture.
            RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
            descriptor.depthBufferBits = 0;

            var renderer = renderingData.cameraData.renderer;
            source = renderer.cameraColorTarget;

            // Create a temporary render texture using the descriptor from above.
            cmd.GetTemporaryRT(temporaryRTIdA , descriptor, FilterMode.Bilinear);
            destinationA = new RenderTargetIdentifier(temporaryRTIdA);
            cmd.GetTemporaryRT(temporaryRTIdB , descriptor, FilterMode.Bilinear);
            destinationB = new RenderTargetIdentifier(temporaryRTIdB);
        }
    
        // The actual execution of the pass. This is where custom rendering occurs.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // Skipping post processing rendering inside the scene View
            if(renderingData.cameraData.isSceneViewCamera)
                return;
        
            // Here you get your materials from your custom class
            // (It's up to you! But here is how I did it)
            var materials = CustomPostProcessingMaterials.Instance;
            if (materials == null)
            {
                Debug.LogError("Custom Post Processing Materials instance is null");
                return;
            }
        
            CommandBuffer cmd = CommandBufferPool.Get("Custom Post Processing");
            cmd.Clear();

            // This holds all the current Volumes information
            // which we will need later
            var stack = VolumeManager.instance.stack;

            #region Local Methods

            // Swaps render destinations back and forth, so that
            // we can have multiple passes and similar with only a few textures
            void BlitTo(Material mat, int pass = 0)
            {
                var first = latestDest;
                var last = first == destinationA ? destinationB : destinationA;
                Blit(cmd, first, last, mat, pass);

                latestDest = last;
            }

            #endregion

            // Starts with the camera source
            latestDest = source;

            //---Custom effect here---
            var customEffect = stack.GetComponent<CustomEffectComponent>();
            // Only process if the effect is active
            if (customEffect.IsActive())
            {
                var customMaterial = materials.customEffect;
                // P.s. optimize by caching the property ID somewhere else
                customMaterial.SetFloat(Shader.PropertyToID("_Intensity"), customEffect.intensity.value);
            
                BlitTo(customMaterial);
            }

            // Add any other custom effect/component you want, in your preferred order
            // Custom effect 2, 3 , ...

            // DONE! Now that we have processed all our custom effects, applies the final result to camera
            Blit(cmd, latestDest, source);
        
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        //Cleans the temporary RTs when we don't need them anymore
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            cmd.ReleaseTemporaryRT(temporaryRTIdA);
            cmd.ReleaseTemporaryRT(temporaryRTIdB);
        }
    }
}