﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
#endif

[SelectionBase]
[ExecuteAlways]
public class ModularTrack : MonoBehaviour {
	
	[ReadOnly]
	public TrackPiece track;
	Vector3 centerPoint = Vector3.zero, pivotPoint = Vector3.zero;
	[ReadOnly]
	public ModularTrack startLink, endLink;
	[ReadOnly]
	public TrackManagerScript trackManager;
	[ReadOnly]
	public ParentConstraint myParentPiece;
	[ReadOnly]
	public bool isItemTrack = false, isRamp = false;
	[ReadOnly]
	public GameObject testObject1, testObject2;

	void Start () {
		#if UNITY_EDITOR
		if (EditorApplication.isPlayingOrWillChangePlaymode) {
			if (!track.pivotObject.gameObject.GetComponent<BoxCollider> ()) {
				track.pivotObject.gameObject.AddComponent<BoxCollider> ();
				track.pivotObject.gameObject.GetComponent<BoxCollider> ().isTrigger = true;
				track.pivotObject.gameObject.GetComponent<BoxCollider>().center = new Vector3(0, 13f, 0f);
				track.pivotObject.gameObject.GetComponent<BoxCollider>().size = new Vector3 (26f, 26f, 1f);
			}
		}
		#else
		if (!track.pivotObject.gameObject.GetComponent<BoxCollider> ()) {
			track.pivotObject.gameObject.AddComponent<BoxCollider> ();
			track.pivotObject.gameObject.GetComponent<BoxCollider> ().isTrigger = true;
			track.pivotObject.localScale = new Vector3 (26f, 1f, 1f);
		}
		#endif
	}

	#if UNITY_EDITOR
    // Update is called once per frame

	public void CreateTrackLink (ModularTrack newTrack) {
		// Update new track values
		newTrack.startLink = endLink;
		newTrack.endLink = endLink.startLink;

		// Update prior link
		endLink.startLink = newTrack;

		// Undate endlink
		endLink = newTrack;
	}

	public void AdjustLinkPositions (ModularTrack modTrack) {
		if (!modTrack.startLink) {
			return;
		}
		PrefabUtility.RecordPrefabInstancePropertyModifications (modTrack.gameObject);
		Undo.RegisterCompleteObjectUndo(modTrack, "Update track positions");
		modTrack.transform.position = modTrack.startLink.track.pivotObject.position;
		modTrack.transform.rotation = modTrack.startLink.track.pivotObject.rotation;
		Physics.SyncTransforms ();
		if (modTrack.endLink) {
			AdjustLinkPositions (modTrack.endLink);
		}
	}

	public bool CollisionCheckLoop (ModularTrack currentTrack) {
		if (!currentTrack.endLink) {
			return true;
		} else if (!currentTrack.endLink.CheckCollision ()) {
			return false;
		} else if (currentTrack.endLink.endLink) {
			return CollisionCheckLoop (currentTrack.endLink);
		} else {
			return true;
		}
	}

	public bool CheckCollision () {
		if (isRamp) {
			return CheckRampCollision ();
		} else if (isItemTrack) {
			return true;
		}else {
			GameObject worldCenter = new GameObject ("Center");
			Instantiate (worldCenter, GetComponent<MeshFilter> ().sharedMesh.bounds.center, Quaternion.identity, worldCenter.transform);
			Transform centerObj = worldCenter.transform.GetChild (0); 

			worldCenter.transform.rotation = transform.rotation;
			worldCenter.transform.localScale = transform.localScale;

			Vector3 centerPos = centerObj.position;

			worldCenter.transform.position = track.pivotObject.transform.position;
			worldCenter.transform.rotation = track.pivotObject.transform.rotation;
			worldCenter.transform.localScale = new Vector3 (transform.localScale.x, 1, -1);

			Vector3 pivotPos = centerObj.position;

			UnityEditor.EditorApplication.delayCall += () => {
				DestroyImmediate (worldCenter);
			};

			centerPoint = centerPos + transform.position;
			pivotPoint = pivotPos;

			Collider[] hitChecks = Physics.OverlapBox (centerPoint, GetComponent<MeshFilter> ().sharedMesh.bounds.size / 2.1f, transform.rotation);
			Collider[] secondaryChecks = Physics.OverlapBox (pivotPoint, GetComponent<MeshFilter> ().sharedMesh.bounds.size / 2.1f, track.pivotObject.transform.rotation);

			//DebugValueChecks ();

			for (int i = 0; i < hitChecks.Length; i++) {
				for (int s = 0; s < secondaryChecks.Length; s++) {
					if (hitChecks [i].transform == transform || hitChecks[i].tag != "TrackPiece") {
						continue;
					}
					if (hitChecks [i].transform.IsChildOf (transform)) {
						continue;
					}
					if (hitChecks [i] == secondaryChecks [s]) {
						if (endLink == hitChecks [i].GetComponent<ModularTrack> ()) {
							continue;
						} else {
							//Debug.Log (gameObject.name + " at " + gameObject.transform.position + ", " + gameObject.transform.eulerAngles + " & " + hitChecks [i].name + " at " + hitChecks [i].transform.position + ", " + hitChecks [i].transform.eulerAngles);
							return false;
						}
					}
				}
			}
		}

		return true;
	}

	public bool CheckRampCollision () {
		
		for (int r = 0; r < transform.childCount; r++) {
			GameObject worldCenter = new GameObject ("Center");
			Instantiate (worldCenter, transform.GetChild(r).GetComponent<MeshFilter> ().sharedMesh.bounds.center, Quaternion.identity, worldCenter.transform);
			Transform centerObj = worldCenter.transform.GetChild (0); 

			worldCenter.transform.rotation = transform.GetChild(r).rotation;
			worldCenter.transform.localScale = transform.GetChild(r).localScale;

			Vector3 centerPos = centerObj.position;

			worldCenter.transform.position = transform.GetChild(r).GetChild(0).position;
			worldCenter.transform.rotation = transform.GetChild(r).GetChild(0).rotation;
			worldCenter.transform.localScale = new Vector3 (transform.GetChild(r).localScale.x, 1, -1);

			Vector3 pivotPos = centerObj.position;

			UnityEditor.EditorApplication.delayCall += () => {
				DestroyImmediate (worldCenter);
			};

			centerPoint = centerPos + transform.GetChild(r).position;
			pivotPoint = pivotPos;

			Collider[] hitChecks = Physics.OverlapBox (centerPoint, transform.GetChild(r).GetComponent<MeshFilter> ().sharedMesh.bounds.size / 2.1f, transform.GetChild(r).rotation);
			Collider[] secondaryChecks = Physics.OverlapBox (pivotPoint, transform.GetChild(r).GetComponent<MeshFilter> ().sharedMesh.bounds.size / 2.1f, transform.GetChild(r).GetChild(0).rotation);

			//DebugValueChecks ();

			for (int i = 0; i < hitChecks.Length; i++) {
				for (int s = 0; s < secondaryChecks.Length; s++) {
					if (hitChecks [i].transform == transform || hitChecks[i].tag != "TrackPiece") {
						continue;
					}
					if (hitChecks [i].transform.IsChildOf (transform)) {
						continue;
					}
					if (hitChecks [i] == secondaryChecks [s]) {
						if (endLink == hitChecks [i].GetComponent<ModularTrack> ()) {
							continue;
						} else {
							//Debug.Log (gameObject.name + " at " + gameObject.transform.position + ", " + gameObject.transform.eulerAngles + " & " + hitChecks [i].name + " at " + hitChecks [i].transform.position + ", " + hitChecks [i].transform.eulerAngles);
							return false;
						}
					}
				}
			}
		}

		return true;
	}

	void OnDestroy () {

		if(Time.frameCount == 0 || DoNotDelete.EditorApplicationQuit){
			//Debug.Log("gotcha");
			return;
		}

		if (EditorApplication.isPlayingOrWillChangePlaymode || (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying)) {
			return;
		}

		if (testObject1) {
			Undo.RegisterCompleteObjectUndo(testObject1, "Test Undo 1");
			testObject1.GetComponent<ModularTrack> ().testObject2 = testObject2;
		}
		if (testObject2) {
			Undo.RegisterCompleteObjectUndo(testObject2, "Test Undo 2");
			testObject2.GetComponent<ModularTrack> ().testObject1 = testObject1;
		}

		// Update the start link
		if (startLink) {
			PrefabUtility.RecordPrefabInstancePropertyModifications (startLink.gameObject);
			Undo.RegisterCompleteObjectUndo(startLink, "Save Start Link Values");
			startLink.endLink = endLink;
		}

		// Handle new end link
		if (endLink) {
			PrefabUtility.RecordPrefabInstancePropertyModifications (endLink.gameObject);
			Undo.RegisterCompleteObjectUndo(endLink, "Save End Link Values");
			// Assign the new link
			endLink.startLink = startLink;

			// Remove itself as the endlink's source
			endLink.GetComponent<ParentConstraint> ().RemoveSource (0);

			// Adjust Positions Quickly
			AdjustLinkPositions (endLink);

			// Create a new source using the startlink
			ConstraintSource startSource = new ConstraintSource ();
			if (startLink) {
				PrefabUtility.RecordPrefabInstancePropertyModifications (startLink.gameObject);
				Undo.RegisterCompleteObjectUndo(startLink, "Save Start Link Values");
				startSource.sourceTransform = startLink.track.pivotObject;
				startSource.weight = 1;
			}

			if(!CollisionCheckLoop(endLink)){
				// Makes student aware of error
				if (EditorUtility.DisplayDialog("Delete Track Piece Error", "Cannont delete this track piece because it causes other pieces to collide with the track.", "Ok")) {
					//Debug.Log ("oh no");
					//Undo.PerformUndo ();
					/*
					GameObject newTrackObj = GameObject.Instantiate (gameObject, transform.position, transform.rotation);
					ModularTrack newTrack = newTrackObj.GetComponent<ModularTrack>();
					newTrackObj.name = gameObject.name;
					newTrackObj.transform.SetSiblingIndex(transform.GetSiblingIndex());

					//activate object
					newTrackObj.SetActive(true);

					// Re-update links
					startLink.endLink = newTrack;
					endLink.startLink = newTrack;
					newTrack.endLink = endLink;
					newTrack.startLink = startLink;

					// Re-update constraints
					// Create a new source using the startlink
					ConstraintSource originalSource = new ConstraintSource ();
					originalSource.sourceTransform = newTrack.track.pivotObject;
					originalSource.weight = 1;

					// Apply the changes to the endLink
					newTrack.endLink.GetComponent<ParentConstraint>().AddSource (originalSource);

					// Apply the changes to the replacement piece
					GetComponent<ParentConstraint>().AddSource (startSource);
					GetComponent<ParentConstraint>().locked = true;
					GetComponent<ParentConstraint>().constraintActive = true;
					*/
				}
			} else {
				// Apply the changes
				endLink.GetComponent<ParentConstraint>().AddSource (startSource);
			}
		}
		//Undo.DestroyObjectImmediate (this);
	}

	void OnDrawGizmos () {
		//Gizmos.DrawSphere (transform.position, 0.5f);
	}

	void DebugValueChecks () {
		Debug.Log ("Size: " + GetComponent<MeshFilter> ().sharedMesh.bounds.size);
		Debug.Log ("Center Main: " + centerPoint);
		Debug.Log ("Pivot Center: " + pivotPoint);
		Debug.Log ("Center of Mesh: " + GetComponent<MeshFilter> ().sharedMesh.bounds.center);
	}

	#endif
}


[System.Serializable]
public class TrackPiece {
	public Transform mainObject, pivotObject;
}

/*
[System.Serializable]
public class LinkPiece {
	public Transform mainObject, pivotObject;
}

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
#endif

[SelectionBase]
[ExecuteAlways]
public class ChainLink : MonoBehaviour {

	public LinkPiece link;
	public ChainLink startLink, endLink;

	#if UNITY_EDITOR

	void OnDestroy () {

		// Stops from running when destroying/recreating objects
		if(Time.frameCount == 0){
			return;
		}

		//Same as above along with preventing this from running in Playmode
		if (EditorApplication.isPlayingOrWillChangePlaymode || (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying)) {
			return;
		}

		// Update the start link
		if (startLink) {
			PrefabUtility.RecordPrefabInstancePropertyModifications (startLink.gameObject);
			Undo.RegisterCompleteObjectUndo(startLink, "Save Start Link Values");
			startLink.endLink = endLink;
		}

		// Handle new end link
		if (endLink) {
			PrefabUtility.RecordPrefabInstancePropertyModifications (endLink.gameObject);
			Undo.RegisterCompleteObjectUndo(endLink, "Save End Link Values");
			// Assign the new link
			endLink.startLink = startLink;

			// Remove itself as the endlink's source
			endLink.GetComponent<ParentConstraint> ().RemoveSource (0);

			// Adjust Positions Quickly
			AdjustLinkPositions (endLink);

			// Create a new source using the startlink
			ConstraintSource startSource = new ConstraintSource ();
			if (startLink) {
				PrefabUtility.RecordPrefabInstancePropertyModifications (startLink.gameObject);
				Undo.RegisterCompleteObjectUndo(startLink, "Save Start Link Values");
				startSource.sourceTransform = startLink.link.pivotObject;
				startSource.weight = 1;
			}

			endLink.GetComponent<ParentConstraint>().AddSource (startSource);
		}
	}

	#endif
}
*/