using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Leap.Unity.RuntimeGizmos
{
	[ExecuteInEditMode]
	public class RuntimeGizmoManager : MonoBehaviour
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public static event Action<RuntimeGizmoDrawer> OnPostRenderGizmos;

		public static bool TryGetGizmoDrawer(out RuntimeGizmoDrawer drawer)
		{
			drawer = RuntimeGizmoManager._backDrawer;
			if (drawer != null)
			{
				drawer.ResetMatrixAndColorState();
				return true;
			}
			return false;
		}

		public static bool TryGetGizmoDrawer(GameObject attatchedGameObject, out RuntimeGizmoDrawer drawer)
		{
			drawer = RuntimeGizmoManager._backDrawer;
			if (drawer != null && !RuntimeGizmoManager.areGizmosDisabled(attatchedGameObject.transform))
			{
				drawer.ResetMatrixAndColorState();
				return true;
			}
			return false;
		}

		protected virtual void OnValidate()
		{
			if (this._gizmoShader == null)
			{
				this._gizmoShader = Shader.Find("Hidden/Runtime Gizmos");
			}
			if (new Material(this._gizmoShader)
			{
				hideFlags = HideFlags.HideAndDontSave
			}.passCount != 4)
			{
				UnityEngine.Debug.LogError("Shader " + this._gizmoShader + " does not have 4 passes and cannot be used as a gizmo shader.");
				this._gizmoShader = Shader.Find("Hidden/Runtime Gizmos");
			}
			if (this._sphereMesh == null)
			{
				GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
				gameObject.hideFlags = HideFlags.HideAndDontSave;
				this._sphereMesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
				gameObject.GetComponent<MeshFilter>().sharedMesh = null;
			}
			if (RuntimeGizmoManager._frontDrawer != null && RuntimeGizmoManager._backDrawer != null)
			{
				this.assignDrawerParams();
			}
		}

		protected virtual void Reset()
		{
			this._gizmoShader = Shader.Find("Hidden/Runtime Gizmos");
		}

		protected virtual void OnEnable()
		{
			if (!this._enabledForBuild)
			{
				base.enabled = false;
				return;
			}
			RuntimeGizmoManager._frontDrawer = new RuntimeGizmoDrawer();
			RuntimeGizmoManager._backDrawer = new RuntimeGizmoDrawer();
			RuntimeGizmoManager._frontDrawer.BeginGuard();
			if (this._gizmoShader == null)
			{
				this._gizmoShader = Shader.Find("Hidden/Runtime Gizmos");
			}
			this.generateMeshes();
			this.assignDrawerParams();
			Camera.onPostRender = (Camera.CameraCallback)Delegate.Remove(Camera.onPostRender, new Camera.CameraCallback(this.onPostRender));
			Camera.onPostRender = (Camera.CameraCallback)Delegate.Combine(Camera.onPostRender, new Camera.CameraCallback(this.onPostRender));
		}

		protected virtual void OnDisable()
		{
			RuntimeGizmoManager._frontDrawer = null;
			RuntimeGizmoManager._backDrawer = null;
			Camera.onPostRender = (Camera.CameraCallback)Delegate.Remove(Camera.onPostRender, new Camera.CameraCallback(this.onPostRender));
		}

		protected virtual void Update()
		{
			SceneManager.GetActiveScene().GetRootGameObjects(this._objList);
			for (int i = 0; i < this._objList.Count; i++)
			{
				GameObject gameObject = this._objList[i];
				gameObject.GetComponentsInChildren<IRuntimeGizmoComponent>(false, this._gizmoList);
				for (int j = 0; j < this._gizmoList.Count; j++)
				{
					if (!RuntimeGizmoManager.areGizmosDisabled((this._gizmoList[j] as Component).transform))
					{
						RuntimeGizmoManager._backDrawer.ResetMatrixAndColorState();
						try
						{
							this._gizmoList[j].OnDrawRuntimeGizmos(RuntimeGizmoManager._backDrawer);
						}
						catch (Exception exception)
						{
							UnityEngine.Debug.LogException(exception);
						}
					}
				}
			}
			this._readyForSwap = true;
		}

		protected void onPostRender(Camera camera)
		{
			if (this._readyForSwap)
			{
				if (RuntimeGizmoManager.OnPostRenderGizmos != null)
				{
					RuntimeGizmoManager._backDrawer.ResetMatrixAndColorState();
					RuntimeGizmoManager.OnPostRenderGizmos(RuntimeGizmoManager._backDrawer);
				}
				RuntimeGizmoDrawer backDrawer = RuntimeGizmoManager._backDrawer;
				RuntimeGizmoManager._backDrawer = RuntimeGizmoManager._frontDrawer;
				RuntimeGizmoManager._frontDrawer = backDrawer;
				RuntimeGizmoManager._frontDrawer.BeginGuard();
				RuntimeGizmoManager._backDrawer.EndGuard();
				this._readyForSwap = false;
				RuntimeGizmoManager._backDrawer.ClearAllGizmos();
			}
			RuntimeGizmoManager._frontDrawer.DrawAllGizmosToScreen();
		}

		protected static bool areGizmosDisabled(Transform transform)
		{
			bool result = false;
			do
			{
				RuntimeGizmoToggle componentInParent = transform.GetComponentInParent<RuntimeGizmoToggle>();
				if (componentInParent == null)
				{
					break;
				}
				if (!componentInParent.enabled)
				{
					goto Block_2;
				}
				transform = transform.parent;
			}
			while (transform != null);
			return result;
			Block_2:
			result = true;
			return result;
		}

		private void assignDrawerParams()
		{
			if (this._gizmoShader != null)
			{
				RuntimeGizmoManager._frontDrawer.gizmoShader = this._gizmoShader;
				RuntimeGizmoManager._backDrawer.gizmoShader = this._gizmoShader;
			}
			RuntimeGizmoManager._frontDrawer.sphereMesh = this._sphereMesh;
			RuntimeGizmoManager._frontDrawer.cubeMesh = this._cubeMesh;
			RuntimeGizmoManager._frontDrawer.wireSphereMesh = this._wireSphereMesh;
			RuntimeGizmoManager._frontDrawer.wireCubeMesh = this._wireCubeMesh;
			RuntimeGizmoManager._backDrawer.sphereMesh = this._sphereMesh;
			RuntimeGizmoManager._backDrawer.cubeMesh = this._cubeMesh;
			RuntimeGizmoManager._backDrawer.wireSphereMesh = this._wireSphereMesh;
			RuntimeGizmoManager._backDrawer.wireCubeMesh = this._wireCubeMesh;
		}

		private void generateMeshes()
		{
			this._cubeMesh = new Mesh();
			this._cubeMesh.name = "RuntimeGizmoCube";
			this._cubeMesh.hideFlags = HideFlags.HideAndDontSave;
			List<Vector3> list = new List<Vector3>();
			List<int> list2 = new List<int>();
			Vector3[] array = new Vector3[]
			{
				Vector3.forward,
				Vector3.right,
				Vector3.up
			};
			for (int i = 0; i < 3; i++)
			{
				this.addQuad(list, list2, array[i % 3], -array[(i + 1) % 3], array[(i + 2) % 3]);
				this.addQuad(list, list2, -array[i % 3], array[(i + 1) % 3], array[(i + 2) % 3]);
			}
			this._cubeMesh.SetVertices(list);
			this._cubeMesh.SetIndices(list2.ToArray(), MeshTopology.Quads, 0);
			this._cubeMesh.RecalculateNormals();
			this._cubeMesh.RecalculateBounds();
			this._cubeMesh.UploadMeshData(true);
			this._wireCubeMesh = new Mesh();
			this._wireCubeMesh.name = "RuntimeWireCubeMesh";
			this._wireCubeMesh.hideFlags = HideFlags.HideAndDontSave;
			list.Clear();
			list2.Clear();
			for (int j = 1; j >= -1; j -= 2)
			{
				for (int k = 1; k >= -1; k -= 2)
				{
					for (int l = 1; l >= -1; l -= 2)
					{
						list.Add(0.5f * new Vector3((float)j, (float)k, (float)l));
					}
				}
			}
			this.addCorner(list2, 0, 1, 2, 4);
			this.addCorner(list2, 3, 1, 2, 7);
			this.addCorner(list2, 5, 1, 4, 7);
			this.addCorner(list2, 6, 2, 4, 7);
			this._wireCubeMesh.SetVertices(list);
			this._wireCubeMesh.SetIndices(list2.ToArray(), MeshTopology.Lines, 0);
			this._wireCubeMesh.RecalculateBounds();
			this._wireCubeMesh.UploadMeshData(true);
			this._wireSphereMesh = new Mesh();
			this._wireSphereMesh.name = "RuntimeWireSphereMesh";
			this._wireSphereMesh.hideFlags = HideFlags.HideAndDontSave;
			list.Clear();
			list2.Clear();
			int num = 96;
			for (int m = 0; m < 32; m++)
			{
				float f = 6.28318548f * (float)m / 32f;
				float num2 = 0.5f * Mathf.Cos(f);
				float num3 = 0.5f * Mathf.Sin(f);
				for (int n = 0; n < 3; n++)
				{
					list2.Add((m * 3 + n) % num);
					list2.Add((m * 3 + n + 3) % num);
				}
				list.Add(new Vector3(num2, num3, 0f));
				list.Add(new Vector3(0f, num2, num3));
				list.Add(new Vector3(num2, 0f, num3));
			}
			this._wireSphereMesh.SetVertices(list);
			this._wireSphereMesh.SetIndices(list2.ToArray(), MeshTopology.Lines, 0);
			this._wireSphereMesh.RecalculateBounds();
			this._wireSphereMesh.UploadMeshData(true);
		}

		private void addQuad(List<Vector3> verts, List<int> indexes, Vector3 normal, Vector3 axis1, Vector3 axis2)
		{
			indexes.Add(verts.Count);
			indexes.Add(verts.Count + 1);
			indexes.Add(verts.Count + 2);
			indexes.Add(verts.Count + 3);
			verts.Add(0.5f * (normal + axis1 + axis2));
			verts.Add(0.5f * (normal + axis1 - axis2));
			verts.Add(0.5f * (normal - axis1 - axis2));
			verts.Add(0.5f * (normal - axis1 + axis2));
		}

		private void addCorner(List<int> indexes, int a, int b, int c, int d)
		{
			indexes.Add(a);
			indexes.Add(b);
			indexes.Add(a);
			indexes.Add(c);
			indexes.Add(a);
			indexes.Add(d);
		}

		public const string DEFAULT_SHADER_NAME = "Hidden/Runtime Gizmos";

		public const int CIRCLE_RESOLUTION = 32;

		[Tooltip("Should the gizmos be visible in the game view.")]
		[SerializeField]
		protected bool _displayInGameView = true;

		[Tooltip("Should the gizmos be visible in a build.")]
		[SerializeField]
		protected bool _enabledForBuild = true;

		[Tooltip("The mesh to use for the filled sphere gizmo.")]
		[SerializeField]
		protected Mesh _sphereMesh;

		[Tooltip("The shader to use for rendering gizmos.")]
		[SerializeField]
		protected Shader _gizmoShader;

		protected Mesh _cubeMesh;

		protected Mesh _wireCubeMesh;

		protected Mesh _wireSphereMesh;

		protected static RuntimeGizmoDrawer _backDrawer;

		protected static RuntimeGizmoDrawer _frontDrawer;

		private bool _readyForSwap;

		private List<GameObject> _objList = new List<GameObject>();

		private List<IRuntimeGizmoComponent> _gizmoList = new List<IRuntimeGizmoComponent>();
	}
}
