﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.Experimental;
#endif

[ExecuteInEditMode]
[RequireComponent(typeof(LootRandomizer))]
public class LevelEditor : MonoBehaviour {
	[Header("Core Level Components")]
	[ReadOnly]
	public GameObject dummyPlayer;
	[ReadOnly]
	public Terrain TerrainMain;

	[HideInInspector]
	public bool levelError = false;
	//bool cleared = false;
	//string lastErrorMessage = "";

	[Header("Manual Save and Export")]

	[BoolToButton("savePrefab")] public bool savePrefab = false;

	[BoolToButton("")] public bool emptyBool = false;

	[BoolToButton("exportLevel")] public bool exportLevel = false;

	[BoolToButton("")] public bool emptyBool2 = false;

	[Header("Autosave Settings")]
	[Tooltip("The number of seconds remaining until the next autosave.")]
	[ReadOnly]
	public string TimeRemaining = "Autosave Disabled";
	[Tooltip("The number of seconds between autosave prompts. Setting this value to Zero will disable the autosave function.")]
	[Range(0, 600)]
	[SerializeField] public float AutosaveInterval = 100;


	private float m_autosaveTime = 1000;
	private bool m_initialSave = false;
	private string m_myLevelName = "";
	private double m_editorTime;
	private float m_editorDeltaTime = 0;

	[Header("Editor Options")]
	public bool ShowLevelBounds = false;
    
	[HideInInspector]
	public bool holdUpdate = true;
	[HideInInspector]
	public List<Transform> objs;
	[HideInInspector]
	public List<Component> coreComponents;

	[Header("Debug")]
	[SerializeField] [ReadOnly] string m_debugDesignation = "[Level Editor] ";
	[SerializeField] bool m_enableDebugMessages = false;

	void Awake () {
#if UNITY_EDITOR
		exportLevel = false;
		holdUpdate = true;
		HideHierarchyElements ();
#endif
#if UNITY_STANDALONE
		if (Application.platform == RuntimePlatform.WebGLPlayer)
		{
			SearchAndDestroyParticles(transform);
			ToggleAllGPUInstancing(transform, false);
		}
		else
			ToggleAllGPUInstancing(transform, true);
		SearchAndDestroyOldWands(transform);
#endif
	}

#if UNITY_EDITOR
	void Start () {
		exportLevel = false;
		if (EditorApplication.isPlayingOrWillChangePlaymode) {
			if (dummyPlayer) {
				dummyPlayer.SetActive (false);
			}
		} else if (dummyPlayer) {
			dummyPlayer.SetActive (true);
			holdUpdate = true;
		}
	}

	void OnValidate()
    {
		if (AutosaveInterval != 0 && AutosaveInterval < 60) { AutosaveInterval = 60; }
		if (m_myLevelName == "" && TerrainMain) 
		{ 
			m_myLevelName = TerrainMain.name.Replace(" Terrain", "");
		}
    }

	void Update () 
	{
		// CHECK FOR ERRORS //
		#region

		// Cancel all correction routines if we have errors
		if (CheckBasics())
		{
			Debug.LogError("Level Editor: Setup error.");
			return;
		}

		#endregion

		if (EditorApplication.isPlayingOrWillChangePlaymode) 
		{
			if (levelError)
			{
				EditorUtility.DisplayDialog("Level Setup Error", "The level cannot be tested because there is something wrong with the level format. Check the error log in edit mode to see what's wrong.", "Ok");
				Debug.DebugBreak();
				Debug.Break();
            }

			if (EditorApplication.isPlaying)
				return;
		} 
		else
        {
            UpdateEditorTime();

            // ADJUST EDITED COMPONENTS //
		#region

            // Snap the terrain to the edges of the level
            SnapTerrainEdges();

            // Snap the level to the origin
            SnapVals();

		#endregion


            // delay saving and other updates
            if (holdUpdate)
            {
                holdUpdate = false;
                return;
            }

            // make the objects children of the main prefab
            AdoptChildren();

            // remove empty transforms
            objs.RemoveAll(Transform => Transform == null);

            // Keep the gameObjects and Prefabs inside the level
            KeepTheKidsInside(transform);

            HandleAutosave();

            if (savePrefab)
            {
                Transform selectedTransform = Selection.activeTransform;
                SavePrefab(true);
                Selection.activeTransform = selectedTransform;
            }
            else if (exportLevel)
            {
                ExportLevelPackage(true);
            }

        }
    }
    

    /// <summary>
    /// Handles autosave behaviors
    /// </summary>
    private void HandleAutosave()
    {
        // If our autosave interval is greater than zero
        if (AutosaveInterval > 0)
        {
            // Clamp the autosave time to the interval
            if (m_autosaveTime > AutosaveInterval)
                m_autosaveTime = AutosaveInterval;

            // If the timer hasn't hit zero yet...
            if (m_autosaveTime > 0)
            {
                // Decrement the timer by the time between GUI updates (this can be several seconds at a time)
                m_autosaveTime -= m_editorDeltaTime;

                // Display the time remaining until the next update in the inspector
                TimeRemaining = Mathf.FloorToInt(m_autosaveTime).ToString() + " seconds";
            }

            // If the autosave time is less than or equal to zero...
            if (m_autosaveTime <= 0)
            {
                // Display the autosave ask dialog and save if the user wants to
                if (EditorUtility.DisplayDialog("Autosave Level", "Would you like to save? Saving will take a few seconds.", "Yes", "No"))
                    savePrefab = true;

                // Reset the autosave timer to the interval
                m_autosaveTime = AutosaveInterval;
            }
        }
        else
        {
            // Autosave timer display is set to this value if we are not tracking time.
            TimeRemaining = "Autosave Disabled";
        }
    }

    private void UpdateEditorTime()
    {
        if (m_editorTime == new double())
        {
            m_editorTime = EditorApplication.timeSinceStartup;
        }

        m_editorDeltaTime = (float)(EditorApplication.timeSinceStartup - m_editorTime);
        m_editorTime = EditorApplication.timeSinceStartup;
    }

    void AdoptChildren () 
	{
		// create an array of all the transforms in the Scene
		Transform[] allObjs = FindObjectsOfType<Transform> ();
		// cycle through all the transforms and set the Level Prefab as the parent object (not including Canvases and Cameras)
		foreach (Transform obj in allObjs)
			if (obj.parent == null && obj.tag != "Safe")
				obj.SetParent (transform);
	}

    /// <summary>
    /// Returns true if basic level information is not set up correctly.
    /// </summary>
    /// <returns></returns>
	bool CheckBasics () 
	{
        levelError = false;

        // Checks if terrain is set up correctly
        if (!AreTerrainsCorrect())
        {
            levelError = true;

			if (m_enableDebugMessages)
				Debug.LogError(m_debugDesignation + "Terrains broken");
        }

        // Checks if the dummy player is set up correctly
        if (!CheckDummies())
        {
            levelError = true;

			if (m_enableDebugMessages)
				Debug.LogError(m_debugDesignation + "Dummies broken");
        }
        
		return levelError;
	}

	bool AreTerrainsCorrect()
    {
		if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "GameIsland")
			return true;

		// Set start value for number of terrains
		int terrainCount = 0;

		// Get all of the terrains in the scene
		Terrain[] terrains = Terrain.FindObjectsOfType<Terrain>();

		// Cycle through the objects to find terrain
		foreach (Terrain t in terrains)
		{

			// Increase the terrain count
			terrainCount++;

			// Make the first terrain the main one
			if (!TerrainMain)
			{
				TerrainMain = t.GetComponent<Terrain>();
			}

			// Remove any additional terrains
			if (TerrainMain && TerrainMain != t)
			{
				EditorApplication.delayCall += () => { DestroyImmediate(t.gameObject); };
				terrainCount--;
			}

			// Set the terrain count to 0 if there's no terrain
			if (!TerrainMain)
			{
				terrainCount = 0;
			}

			// Remove the object if it is a duplicate of the Level Prefab
			if (t.GetComponent<LevelEditor>())
			{
				EditorApplication.delayCall += () => { DestroyImmediate(t.gameObject); };
			}
		}

		// Display terrain error message
		if (terrainCount == 0)
		{
			if (m_enableDebugMessages)
				Debug.LogError(m_debugDesignation + "There is no Terrain attached to the gameobject. Please add a unique Terrain from the Level Prefabs folder to the gameobject.");
			return false;
		}
        else if (terrainCount > 1)
        {
			if (m_enableDebugMessages)
				Debug.LogError(m_debugDesignation + "Too many terrains in scene. Please ensure you are using a scene with only a single piece of terrain in it.");
            return false;
        }

        return true;
	}

	bool CheckDummies()
	{
		// Set start value for number of terrains
		int dummyCount = 0;

		// Get all of the dummies in the scene
		GameObject[] dummies = GameObject.FindGameObjectsWithTag("Dummy");

		// Cycle through the dummies
		foreach (GameObject dummy in dummies)
		{
			// Increase the dummy count
			dummyCount++;

			// Make the first dummy the main one
			if (!dummyPlayer)
			{
				dummyPlayer = dummy;
			}

			// Remove any additional dummies
			if (dummyPlayer && dummyPlayer != dummy)
			{
				UnityEditor.EditorApplication.delayCall += () => { DestroyImmediate(dummy); };
				dummyCount--;
			}

			// Set the dummy count to 0 if there's no dummy
			if (!dummyPlayer)
			{
				dummyCount = 0;
			}
		}

        // If we are in edit mode and NOT playtesting...
        if (!EditorApplication.isPlaying)
        {
		    // Display dummy error message
		    if (dummyCount == 0)
		    {
				if (m_enableDebugMessages)
					Debug.LogError(m_debugDesignation + "There is no Dummy attached to the gameobject. Please add a unique Dummy from the Level Prefabs folder to the gameobject.");
			    
				return false;
		    }
		    else if (dummyCount > 1)
            {
				if (m_enableDebugMessages)
					Debug.LogError(m_debugDesignation + "There are multiple Dummy Players in the scene. Please remove any extra dummies from the scene.");
			    
				return false;
		    }
        }

		return true;
	}

	void KeepTheKidsInside (Transform parent) 
	{
		foreach (Transform kid in parent) 
		{
            // Skip the spell catalog
            if (kid.GetComponent<BattleRoyale.Spells.SpellCatalog>())
                continue;

			// Get the index of the object from the list, and if it's not in the list, add it
			int index = GetObjectIndex (kid);

			// Determine the object type
			string objectType = CheckObjectComponents (kid);

			// Contain all the objects in the level
			ContainLevelObjects (objs [index].GetComponent<Collider> ());

			// Update the object based on whether or not it is inside the level
			if (objectType == "Tinker") {
				Bounds tinkerBounds = new Bounds (kid.position, Vector3.zero);
				foreach (Collider tinker in kid.GetComponentsInChildren<Collider>()) {
					
					if (tinker.GetComponent<Tinkercad> ())
						continue;
					
					if (!tinker.name.Contains ("group")) {
						ContainLevelObjects (tinker);
						continue;
					}

					tinkerBounds.Encapsulate (tinker.bounds);
				}
				Vector3 tinkerCenter = tinkerBounds.center - kid.transform.position;
				tinkerBounds.center = tinkerCenter;
				kid.GetComponent<BoxCollider> ().size = new Vector3 (tinkerBounds.size.x / kid.localScale.x, tinkerBounds.size.y / kid.localScale.y, tinkerBounds.size.z / kid.localScale.z);
				kid.GetComponent<BoxCollider> ().center = new Vector3 (tinkerBounds.center.x / kid.localScale.x, tinkerBounds.center.y / kid.localScale.y, tinkerBounds.center.z / kid.localScale.z);
			}

			// if the object is not a special type, cycle through its children
			if (objectType == "" && kid.childCount > 0)
				KeepTheKidsInside (kid);
		}
	}

	void ContainLevelObjects (Collider myCollider) 
	{
		if (!myCollider)
			return;
		Vector3 corner1 = new Vector3 (myCollider.bounds.max.x, myCollider.bounds.max.y, myCollider.bounds.min.z);
		Vector3 corner2 = new Vector3 (myCollider.bounds.max.x, myCollider.bounds.min.y, myCollider.bounds.min.z);
		Vector3 corner3 = new Vector3 (myCollider.bounds.max.x, myCollider.bounds.min.y, myCollider.bounds.max.z);
		Vector3 corner4 = new Vector3 (myCollider.bounds.min.x, myCollider.bounds.max.y, myCollider.bounds.max.z);
		Vector3 corner5 = new Vector3 (myCollider.bounds.min.x, myCollider.bounds.max.y, myCollider.bounds.min.z);
		Vector3 corner6 = new Vector3 (myCollider.bounds.min.x, myCollider.bounds.min.y, myCollider.bounds.max.z);

		Vector3 plusX = (myCollider.bounds.max + corner2) / 2f;
		Vector3 negX = (myCollider.bounds.min + corner4) / 2f;

		Vector3 plusY = (myCollider.bounds.max + corner5) / 2f;
		Vector3 negY = (myCollider.bounds.min + corner3) / 2f;

		Vector3 plusZ = (myCollider.bounds.max + corner6) / 2f;
		Vector3 negZ = (myCollider.bounds.min + corner1) / 2f;

		if (ShowLevelBounds) { DrawDebugBounds(myCollider, corner1, corner2, corner3, corner4, corner5, corner6, plusX, plusY, plusZ, negX, negY, negZ); }

		// determine how far the outer bounds is outside the level
		float plusOutsideZ = 190f - plusZ.z;
		float negOutsideZ = -210f - negZ.z;
		float plusOutsideX = 190f - plusX.x;
		float negOutsideX = -210f - negX.x;
		float plusOutsideY = 200f - plusY.y;
		float negOutsideY = -200f - negY.y;

		// make the position inside the level if it is outside
		if (plusOutsideZ < 0f) {
			myCollider.transform.position += new Vector3 (0f, 0f, plusOutsideZ);
		} else if (negOutsideZ > 0f) {
			myCollider.transform.position += new Vector3 (0f, 0f, negOutsideZ);
		}
		if (plusOutsideX < 0f) {
			myCollider.transform.position += new Vector3 (plusOutsideX, 0f, 0f);
		} else if (negOutsideX > 0f) {
			myCollider.transform.position += new Vector3 (negOutsideX, 0f, 0f);
		}
		if (plusOutsideY < 0f) {
			myCollider.transform.position += new Vector3 (0f, plusOutsideY, 0f);
		} else if (negOutsideY > 0f) {
			myCollider.transform.position += new Vector3 (0f, negOutsideY, 0f);
		}

		// determine the largest possible scale for each axis
		Vector3 adjustedScale = new Vector3 (myCollider.transform.localScale.x/myCollider.bounds.size.x,myCollider.transform.localScale.y/myCollider.bounds.size.y, myCollider.transform.localScale.z/myCollider.bounds.size.z);

		// adjust the scale accordingly
		if (myCollider.bounds.size.x > 400f) {
			myCollider.transform.localScale = new Vector3 (adjustedScale.x * 400, myCollider.transform.localScale.y, myCollider.transform.localScale.z);
		}
		if (myCollider.bounds.size.y > 400f) {
			myCollider.transform.localScale = new Vector3 (myCollider.transform.localScale.x, adjustedScale.y * 400, myCollider.transform.localScale.z);
		}
		if (myCollider.bounds.size.z > 400f) {
			myCollider.transform.localScale = new Vector3 (myCollider.transform.localScale.x, myCollider.transform.localScale.y, adjustedScale.z * 400);
		}

	}

	int GetObjectIndex (Transform levelObj) {
		int checkInt = -1;
		if (objs.Count > 0) {
			for (int i = 0; i < objs.Count; i++) {
				if (levelObj == objs[i]) {
					checkInt = i;
					break;
				}
			}
		}
		if (checkInt == -1) {
			objs.Add (levelObj);
			return (GetObjectIndex (levelObj));
		}
		return checkInt;
	}

	// determine the type of object
	string CheckObjectComponents (Transform check) {
		if (check == dummyPlayer.transform)
			return "Empty";
		else if (check.GetComponent<Tinkercad> ()) {
			if (!check.GetComponent<Collider> ()) {
				check.gameObject.AddComponent<BoxCollider> ();
				UnityEditor.EditorApplication.delayCall += () => { check.GetComponent<Collider> ().isTrigger = true; };
			}
			return "Tinker";
		} else if (check.GetComponent<NewWandScript> ())
			return "Wand";
        else if (check.GetComponent<BattleRoyale.Spells.SpellBase> ())
			return "Spell";
        else if (check.GetComponent<PotionScript> ())
			return "Potion";
		else if (check.GetComponent<LootChestScript> ())
			return "Loot";
		else if (check.GetComponent<DiscoBall> ())
			return "Disco";
		else if (check.GetComponent<Collider> ())
			return "";
		else if (check.GetComponent<MeshRenderer> () && !check.GetComponent<Collider> ()) {
			UnityEditor.EditorApplication.delayCall += () => { check.gameObject.AddComponent<MeshCollider> (); };
			return "";
		} else
			return "Empty";

	}

	bool CanUpdate (int objIndex, string objType) {

		if (objType == "Empty")
			return false;

		// Tinkercad objects have Colliders on all their child game objects
		if (objType == "Tinker") {
			foreach (Transform tinker in objs[objIndex]) {

			}
			return true;

			// Disco Balls only have Colliders on two of their children, the rest can be ignored
		} else if (objType == "Disco") {
			foreach (Transform disco in objs[objIndex]) {
				if (disco.GetComponent<Collider> ()) {

				}
			}
			return true;

			// treat Potions and Loot Chests the same as other objects, they are only labled so they don't loop through their children
		} else if (objType == "Potion" || objType == "Loot" || objType == "") {
			return true;
		}

		return true;
	}

	void SavePrefab (bool save) {
		if (m_initialSave) {
			m_myLevelName = gameObject.name;
			m_initialSave = false;
		}

		if (m_myLevelName == "" && gameObject.name == "")
        {
			m_myLevelName = EditorSceneManager.GetActiveScene().name;
		}

		if (m_myLevelName != gameObject.name) {
			gameObject.name = m_myLevelName;
		}

		if (gameObject.activeInHierarchy && save) {

			// Search for an existing level prefab
			GameObject currentPrefab = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/Levels/" + gameObject.name + ".prefab", typeof(GameObject));

			// If one is not found, create a new one
			if (!currentPrefab)
			{
                PrefabUtility.SaveAsPrefabAsset(gameObject, "Assets/Levels/" + gameObject.name + ".prefab");
			}
            else
			{
				PrefabUtility.SaveAsPrefabAssetAndConnect(gameObject, "Assets/Levels/" + gameObject.name + ".prefab", InteractionMode.UserAction);
			}

			PrefabUtility.UnpackPrefabInstance(gameObject, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);

			EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene ());
		}

		savePrefab = false;
		HideHierarchyElements ();
	}

	void ExportLevelPackage (bool export) {
		if (gameObject.activeInHierarchy && export) {
			SavePrefab (true);

			if (EditorUtility.DisplayDialog("Open Black Rocket Arcade", "Battle Royale maps can be uploaded to the Black Rocket arcade to share with your classmates and instructors. Would you like to open the Black Rocket Arcade in a web browser?", "Yes", "No"))
			{
				Application.OpenURL("https://arcade.blackrocket.com/add-an-asset/");
			}

			string filename = gameObject.name.Substring (0, gameObject.name.Length - 7) + ".unitypackage";
			AssetDatabase.ExportPackage ("Assets/Levels/" + gameObject.name + ".prefab", "Asset Packages/" + filename, ExportPackageOptions.IncludeDependencies | ExportPackageOptions.Interactive);
		}
		exportLevel = false;
	}

	void HideHierarchyElements () {
		GameObject canvases = GameObject.FindGameObjectWithTag ("Safe");
		if(canvases){
			canvases.hideFlags = HideFlags.None;
		}
	}

	void SnapTerrainEdges () {

		// Get the terrain heightmap width and height
		int xRes = TerrainMain.terrainData.heightmapWidth;
		int yRes = TerrainMain.terrainData.heightmapHeight;

		// GetHeights = gets the heightmap points of the terrain. Store those values in a float array
		float [,] heights = TerrainMain.terrainData.GetHeights(0, 0, xRes, yRes);

		// Manipulate the height data
		for (int i = 0; i < 257; i++) {
			for (int j = 0; j < 11; j++) {
				heights [0 + j, i] = 0.5f;
				heights [256 - j, i] = 0.5f;
				heights [i, 0 + j] = 0.5f;
				heights [i, 256 - j] = 0.5f;
			}
		}

		// SetHeights to change the terrain heightdata
		TerrainMain.terrainData.SetHeights(0, 0, heights);
	}

	void LockAndHide () {
		Tools.lockedLayers = (1 << LayerMask.NameToLayer ("UI")) |
			(1 << LayerMask.NameToLayer ("Camera")) |
			(1 << LayerMask.NameToLayer ("Level Editor")) |
			(1 << LayerMask.NameToLayer ("MiniMap")) |
			(1 << LayerMask.NameToLayer ("UI Cameras")) |
			(1 << LayerMask.NameToLayer ("BigMap")) |
			(1 << LayerMask.NameToLayer ("HideOnCamera")) |
			(1 << LayerMask.NameToLayer ("OuterBounds"));
		Tools.visibleLayers = ~((1 << LayerMask.NameToLayer ("UI")) |
			(1 << LayerMask.NameToLayer ("Camera")) |
			(1 << LayerMask.NameToLayer ("Level Editor")) |
			(1 << LayerMask.NameToLayer ("MiniMap")) |
			(1 << LayerMask.NameToLayer ("UI Cameras")) |
			(1 << LayerMask.NameToLayer ("BigMap")) |
			(1 << LayerMask.NameToLayer ("HideOnCamera")) |
			(1 << LayerMask.NameToLayer ("OuterBounds")));
	}

	void SnapVals () {
		// Level Prefab Defaults
		transform.position = new Vector3(-10, 0, -10);
		transform.eulerAngles = Vector3.zero;
		transform.localScale = Vector3.one;

		// Terrain Defaults
		TerrainMain.transform.localPosition = Vector3.one * -200;
		TerrainMain.transform.eulerAngles = Vector3.zero;
		TerrainMain.transform.localScale = Vector3.one;

		// Dummy Defaults
		dummyPlayer.transform.localPosition = Vector3.zero;
		dummyPlayer.transform.eulerAngles = Vector3.zero;
		dummyPlayer.transform.localScale = Vector3.one;

		TerrainMain.gameObject.layer = 17;
	}

	void DrawDebugBounds (Collider myCollider,
		Vector3 corner1, Vector3 corner2, Vector3 corner3, Vector3 corner4, Vector3 corner5, Vector3 corner6,
		Vector3 plusX, Vector3 plusY, Vector3 plusZ, Vector3 negX, Vector3 negY, Vector3 negZ) {

		Debug.DrawLine (myCollider.bounds.max, corner1, Color.blue);
		Debug.DrawLine (corner2, corner3, Color.green);
		Debug.DrawLine (myCollider.bounds.max, corner3, Color.blue);
		Debug.DrawLine (corner1, corner2, Color.yellow);

		Debug.DrawLine (corner4, corner5, new Color32 (228, 114, 37, 255));
		Debug.DrawLine (corner3, corner6, Color.green);
		Debug.DrawLine (corner1, corner5, new Color32 (228, 114, 37, 255));
		Debug.DrawLine (corner2, myCollider.bounds.min, Color.red);

		Debug.DrawLine (myCollider.bounds.max, corner4, Color.blue);
		Debug.DrawLine (corner4, corner6, Color.yellow);
		Debug.DrawLine (corner5, myCollider.bounds.min, Color.red);
		Debug.DrawLine (corner6, myCollider.bounds.min, Color.red);

		Debug.DrawLine (plusX, negX, Color.magenta);
		Debug.DrawLine (plusY, negY, Color.magenta);
		Debug.DrawLine (plusZ, negZ, Color.magenta);
	}

	/*
	public static void ClearConsole () {
		var assembly = Assembly.GetAssembly (typeof(SceneView));
		var type = assembly.GetType ("UnityEditor.LogEntries");
		var method = type.GetMethod ("Clear");
		method.Invoke (new object (), null);
	}

	void ErrorStop (bool error) {
		Undo.RecordObject (this, "Set Value");
		this.levelError = error;
		EditorUtility.SetDirty (this);
	}
	*/

#else
		void Start () {
		if (dummyPlayer) {
			dummyPlayer.SetActive (false);
		}
	}
#endif

	void SearchAndDestroyParticles(Transform parent)
	{
		foreach (Transform child in parent)
		{
			if (child.GetComponent<ParticleSystem>())
				Destroy(child.GetComponent<ParticleSystem>());

			SearchAndDestroyParticles(child);
		}
	}
	
	void ToggleAllGPUInstancing(Transform parent, bool _state)
	{
		foreach (Transform child in parent)
		{
			if (child.GetComponent<Renderer>())
				if (child.GetComponent<Renderer>().sharedMaterials.Length > 0)
					foreach (Material _mat in child.GetComponent<Renderer>().sharedMaterials)
						_mat.enableInstancing = _state;

			ToggleAllGPUInstancing(child, _state);
		}
	}
	void SearchAndDestroyOldWands(Transform parent)
	{
		foreach (Transform child in parent)
		{
			if (child.GetComponent<WandScript>())
            {
				Destroy(child);
				continue;
            }

			SearchAndDestroyOldWands(child);
		}
	}
}
