﻿using UnityEngine;
using System.Collections;

namespace AC.Downloads.FirstPerson2D
{

	public class CameraNode2D : MonoBehaviour
	{

		#region Variables

		[SerializeField] private float crossfadeTime = 0.2f;

		[SerializeField] private NodeDirection2D north;
		[SerializeField] private NodeDirection2D east;
		[SerializeField] private NodeDirection2D south;
		[SerializeField] private NodeDirection2D west;

		private enum FacingDirection2D { None, North, East, South, West };
		private FacingDirection2D direction = FacingDirection2D.None;

		private const string navigationMenuName = "Navigation";
		private const string moveForwardButtonName = "Forward";
		private const string moveBackwardButtonName = "Backward";

		private static CameraNode2D activeNode;

		#endregion


		#region UnityStandards

		private void OnEnable ()
		{
			// We'll hook into an event that triggers when switching camera
			EventManager.OnSwitchCamera += OnSwitchCamera;
		}


		private void OnDisable ()
		{
			EventManager.OnSwitchCamera -= OnSwitchCamera;
		}

		#endregion


		#region PublicFunctions

		public void MoveForward ()
		{
			if (activeNode)
				activeNode._MoveForward ();
			else ACDebug.LogWarning ("No active node set - switch to a camera controlled by a CameraNode2D object to activate it.");
		}


		public void MoveBackward ()
		{
			if (activeNode)
				activeNode._MoveBackward ();
			else ACDebug.LogWarning ("No active node set - switch to a camera controlled by a CameraNode2D object to activate it.");
		}


		public void TurnLeft ()
		{
			if (activeNode)
				activeNode._TurnLeft ();
			else ACDebug.LogWarning ("No active node set - switch to a camera controlled by a CameraNode2D object to activate it.");
		}


		public void TurnRight ()
		{
			if (activeNode)
				activeNode._TurnRight ();
			else ACDebug.LogWarning ("No active node set - switch to a camera controlled by a CameraNode2D object to activate it.");
		}

		#endregion


		#region CustomEvents

		private void OnSwitchCamera (_Camera fromCamera, _Camera toCamera, float transitionTime)
		{
			// When we switch camera, we want to learn the active node, current direction, and hide the forward/backward icons if we cannot move in those directions

			if (north.IsMatch (toCamera))
			{
				direction = FacingDirection2D.North;
			}
			else if (east.IsMatch (toCamera))
			{
				direction = FacingDirection2D.East;
			}
			else if (south.IsMatch (toCamera))
			{
				direction = FacingDirection2D.South;
			}
			else if (west.IsMatch (toCamera))
			{
				direction = FacingDirection2D.West;
			}
			else
			{
				direction = FacingDirection2D.None;
			}

			if (direction != FacingDirection2D.None)
			{
				activeNode = this;

				bool canMoveForward = GetDirectionNode (direction).CanMoveForward ();
				PlayerMenus.GetElementWithName (navigationMenuName, moveForwardButtonName).IsVisible = canMoveForward;

				bool canMoveBackward = GetReverseDirectionNode (direction).CanMoveForward ();
				PlayerMenus.GetElementWithName (navigationMenuName, moveBackwardButtonName).IsVisible = canMoveBackward;
			}
			else if (activeNode == this)
			{
				activeNode = null;
			}
		}

		#endregion


		#region PrivateFunctions

		private void _MoveForward ()
		{
			_Camera nextCamera = GetDirectionNode (direction).GetForwardCamera (direction);
			SwitchCamera (nextCamera);
		}


		private void _MoveBackward ()
		{
			_Camera nextCamera = GetReverseDirectionNode (direction).GetForwardCamera (direction);
			SwitchCamera (nextCamera);
		}


		private void _TurnLeft ()
		{
			switch (direction)
			{
				case FacingDirection2D.North:
					SetFacingDirection (FacingDirection2D.West);
					break;

				case FacingDirection2D.East:
					SetFacingDirection (FacingDirection2D.North);
					break;

				case FacingDirection2D.South:
					SetFacingDirection (FacingDirection2D.East);
					break;

				case FacingDirection2D.West:
					SetFacingDirection (FacingDirection2D.South);
					break;
					
				default:
					break;
			}
		}


		private void _TurnRight ()
		{
			switch (direction)
			{
				case FacingDirection2D.North:
					SetFacingDirection (FacingDirection2D.East);
					break;

				case FacingDirection2D.East:
					SetFacingDirection (FacingDirection2D.South);
					break;

				case FacingDirection2D.South:
					SetFacingDirection (FacingDirection2D.West);
					break;

				case FacingDirection2D.West:
					SetFacingDirection (FacingDirection2D.North);
					break;
					
				default:
					break;
			}
		}


		private void SetFacingDirection (FacingDirection2D newDirection)
		{
			direction = newDirection;
			_Camera nextCamera = GetDirectionNode (direction).Camera;
			SwitchCamera (nextCamera);
		}


		private void SwitchCamera (_Camera _camera)
		{
			KickStarter.mainCamera.Crossfade (crossfadeTime, _camera, null);
		}


		private _Camera GetCamera (FacingDirection2D _direction)
		{
			return GetDirectionNode (_direction).Camera;
		}


		private NodeDirection2D GetDirectionNode (FacingDirection2D _direction)
		{
			switch (_direction)
			{
				case FacingDirection2D.North:
					return north;

				case FacingDirection2D.East:
					return east;

				case FacingDirection2D.South:
					return south;

				case FacingDirection2D.West:
					return west;
					
				default:
					return null;
			}
		}


		private NodeDirection2D GetReverseDirectionNode (FacingDirection2D _direction)
		{
			switch (_direction)
			{
				case FacingDirection2D.North:
					return south;

				case FacingDirection2D.East:
					return west;

				case FacingDirection2D.South:
					return north;

				case FacingDirection2D.West:
					return east;
					
				default:
					return null;
			}
		}

		#endregion


		#region PrivateClasses

		[System.Serializable]
		private class NodeDirection2D
		{

			[SerializeField] private _Camera camera;
			[SerializeField] private CameraNode2D node;


			public bool IsMatch (_Camera _camera)
			{
				return (camera == _camera);
			}


			public _Camera GetForwardCamera (FacingDirection2D direction)
			{
				if (node != null)
				{
					_Camera newCamera = node.GetCamera (direction);
					return newCamera;
				}
				return null;
			}


			public bool CanMoveForward ()
			{
				return (node != null);
			}


			public _Camera Camera
			{
				get
				{
					return camera;
				}
			}

		}

		#endregion
		
	}

}