// Copyright (C) 2019-2021 Alexander Bogarsukov. All rights reserved.
// See the LICENSE.md file in the project root for more information.

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

namespace UnityFx.Outline.URP
{
	internal class OutlinePass : ScriptableRenderPass
	{
		//private const string _profilerTag = "OutlinePass";
		//private static readonly ProfilingSampler _profilingSampler = new ProfilingSampler(_profilerTag);

		private readonly OutlineFeature _feature;
		private readonly List<OutlineRenderObject> _renderObjects = new List<OutlineRenderObject>();
		private readonly List<ShaderTagId> _shaderTagIdList = new List<ShaderTagId>();

		private ScriptableRenderer _renderer;

		public OutlinePass(OutlineFeature feature, string[] shaderTags)
		{
			_feature = feature;

			if (shaderTags != null && shaderTags.Length > 0)
			{
				foreach (var passName in shaderTags)
				{
					_shaderTagIdList.Add(new ShaderTagId(passName));
				}
			}
			else
			{
				_shaderTagIdList.Add(new ShaderTagId("UniversalForward"));
				_shaderTagIdList.Add(new ShaderTagId("LightweightForward"));
				_shaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
			}
		}

		public void Setup(ScriptableRenderer renderer)
		{
			_renderer = renderer;
		}

		public class PassData {
			public string featureName;
			public OutlineResources outlineResources;
			public IOutlineSettings outlineSettings;
			public int outlineLayerMask;
			public uint outlineRenderingLayerMask;
			public OutlineLayerCollection outlineLayers;
			public List<OutlineRenderObject> renderObjects;
			public List<ShaderTagId> shaderTagIdList;
		}

        public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
        {
			IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass("Outline Pass", out PassData passData);
			passData.featureName = _feature.FeatureName;
			passData.outlineResources = _feature.OutlineResources;
			passData.outlineSettings = _feature.OutlineSettings;
			passData.outlineLayerMask = _feature.OutlineLayerMask;
			passData.outlineRenderingLayerMask = _feature.OutlineRenderingLayerMask;
			passData.outlineLayers = _feature.OutlineLayers;
			passData.renderObjects = _renderObjects;
			passData.shaderTagIdList = _shaderTagIdList;

			builder.SetRenderFunc<PassData>(ExecuteRender);
        }

		public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
		{

		}

		public static void ExecuteRender(PassData passData, RasterGraphContext context)
		{
			var outlineResources = passData.outlineResources;
			var outlineSettings = passData.outlineSettings;
			var camData = renderingData.cameraData;

			if (passData.outlineLayerMask != 0)
			{
				var cmd = CommandBufferPool.Get(passData.featureName);
				var filteringSettings = new FilteringSettings(RenderQueueRange.all, passData.outlineLayerMask, passData.outlineRenderingLayerMask);
				var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
				var sortingCriteria = camData.defaultOpaqueSortFlags;
				var drawingSettings = CreateDrawingSettings(passData.shaderTagIdList, ref renderingData, sortingCriteria);
				var depthTexture = new RenderTargetIdentifier("_CameraDepthTexture");

				drawingSettings.enableDynamicBatching = true;
				drawingSettings.overrideMaterial = outlineResources.RenderMaterial;

				if (outlineSettings.IsAlphaTestingEnabled())
				{
					drawingSettings.overrideMaterialPassIndex = OutlineResources.RenderShaderAlphaTestPassId;
					cmd.SetGlobalFloat(outlineResources.AlphaCutoffId, outlineSettings.OutlineAlphaCutoff);
				}
				else
				{
					drawingSettings.overrideMaterialPassIndex = OutlineResources.RenderShaderDefaultPassId;
				}

				using (var renderer = new OutlineRenderer(cmd, outlineResources, _renderer.cameraColorTargetHandle, depthTexture/*_renderer.cameraDepth*/, camData.cameraTargetDescriptor))
				{
					renderer.RenderObjectClear(outlineSettings.OutlineRenderMode);
					context.ExecuteCommandBuffer(cmd);
					RendererListParams rParams = new RendererListParams(renderingData.cullResults, drawingSettings, filteringSettings);
					RendererList rList = context.CreateRendererList(ref rParams);
					//context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings, ref renderStateBlock);
					cmd.DrawRendererList(rList);
					cmd.Clear();
					renderer.RenderOutline(outlineSettings);
				}

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

			if (passData.outlineLayers)
			{
				var cmd = CommandBufferPool.Get(OutlineResources.EffectName);
				var depthTexture = new RenderTargetIdentifier("_CameraDepthTexture");

				using (var renderer = new OutlineRenderer(cmd, outlineResources, _renderer.cameraColorTargetHandle, depthTexture /*_renderer.cameraDepth*/, camData.cameraTargetDescriptor))
				{
					passData.renderObjects.Clear();
					passData.outlineLayers.GetRenderObjects(passData.renderObjects);
					renderer.Render(passData.renderObjects);
				}

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