using UnityEngine;
using System.Collections.Generic;

namespace Me.StefS.Utility
{
	//Counterclockwise order
	public enum DiagonalOrientationSide {
		UpLeft = 0,
		DownLeft = 1,
		DownRight = 2,
		UpRight = 3,
	}

	public struct DiagonalConsistedOf{
		public OrientationSide counterClockwiseSide;
		public OrientationSide clockwiseSide;

		public DiagonalConsistedOf(OrientationSide counterClockwiseSide, OrientationSide clockwiseSide){
			this.counterClockwiseSide = counterClockwiseSide;
			this.clockwiseSide = clockwiseSide;
		}
	}

	public static class DiagonalOrientationSideExtensions
	{
		//If in any future you put bidirectional map in this project, use it here
		private static IDictionary<DiagonalOrientationSide, DiagonalOrientationSide> opposites = new Dictionary<DiagonalOrientationSide, DiagonalOrientationSide>{
			{DiagonalOrientationSide.UpLeft, DiagonalOrientationSide.DownRight},
			{DiagonalOrientationSide.UpRight, DiagonalOrientationSide.DownLeft},
			{DiagonalOrientationSide.DownLeft, DiagonalOrientationSide.UpRight},
			{DiagonalOrientationSide.DownRight, DiagonalOrientationSide.UpLeft},
		};

		private static IDictionary<DiagonalOrientationSide, DiagonalConsistedOf> diagonalConsistency = new Dictionary<DiagonalOrientationSide, DiagonalConsistedOf>
		{
			{DiagonalOrientationSide.UpLeft, new DiagonalConsistedOf(OrientationSide.Left, OrientationSide.Up)},
			{DiagonalOrientationSide.DownLeft, new DiagonalConsistedOf(OrientationSide.Down, OrientationSide.Left)},
			{DiagonalOrientationSide.DownRight, new DiagonalConsistedOf(OrientationSide.Right, OrientationSide.Down)},
			{DiagonalOrientationSide.UpRight, new DiagonalConsistedOf(OrientationSide.Up, OrientationSide.Right)},
		};

		private static HashSet<DiagonalOrientationSide> allValues = new HashSet<DiagonalOrientationSide>((DiagonalOrientationSide[])System.Enum.GetValues (typeof(DiagonalOrientationSide)));

		private static bool isDiagonalToDirectionVectorMapInitialized = false;
		private static IDictionary<DiagonalOrientationSide, Vector3> diagonalToDirectionVectorMap;

		public static bool IsValid(this DiagonalOrientationSide side) {
			bool isValid = allValues.Contains (side);
			return isValid;
		}

		public static IEnumerable<DiagonalOrientationSide> GetValues() {
			return allValues;
		}

		public static DiagonalOrientationSide LeftDiagonal(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			return (DiagonalOrientationSide)((int)(diagonal + 1) % 4);
		}

		public static DiagonalOrientationSide RightDiagonal(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			return (DiagonalOrientationSide)((int)(diagonal - 1 + 4) % 4);
		}

		public static DiagonalOrientationSide Opposite(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			return opposites[diagonal];
		}

		public static OrientationSide RightSide(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			return diagonalConsistency[diagonal].clockwiseSide;
		}

		public static OrientationSide LeftSide(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			return diagonalConsistency[diagonal].counterClockwiseSide;
		}

		public static OrientationSide VerticalComponent(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			OrientationSide component;
			if(diagonalConsistency[diagonal].clockwiseSide.IsUpDown()){
				component = diagonalConsistency[diagonal].clockwiseSide;
			}else{
				component = diagonalConsistency[diagonal].counterClockwiseSide;
			}

			return component;
		}	

		public static OrientationSide HorisontalComponent(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}
			
			OrientationSide component;
			if(!diagonalConsistency[diagonal].clockwiseSide.IsUpDown()){
				component = diagonalConsistency[diagonal].clockwiseSide;
			}else{
				component = diagonalConsistency[diagonal].counterClockwiseSide;
			}
			
			return component;
		}
		public static OrientationSide AddToDiagonal(this DiagonalOrientationSide diagonal, DiagonalOrientationSide addToDiagonal) {
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			if(!addToDiagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("addToDiagonal");
			}

			if(opposites[diagonal] == addToDiagonal){
				throw new System.ArgumentOutOfRangeException("Opposing diagonals cannot be added togather");
			}

			if(diagonal.RightDiagonal() == addToDiagonal)
			{
				return diagonal.RightSide();
			}else{
				return diagonal.LeftSide();
			}
		}

		public static Vector3 GetDirection(this DiagonalOrientationSide diagonal){
			if(!diagonal.IsValid()){
				throw new System.ArgumentOutOfRangeException("diagonal");
			}

			if(!isDiagonalToDirectionVectorMapInitialized){
				InitializeDiagonalToDirectionVectorMap();
			}

			return diagonalToDirectionVectorMap[diagonal];
		}

		#region Helper Methods
		private static void InitializeDiagonalToDirectionVectorMap(){
			diagonalToDirectionVectorMap = new Dictionary<DiagonalOrientationSide, Vector3>();
			IEnumerator<DiagonalOrientationSide> iterator = DiagonalOrientationSideExtensions.GetValues().GetEnumerator();
			try{
				while(iterator.MoveNext()){
					DiagonalOrientationSide currentDiagonal = iterator.Current;
					diagonalToDirectionVectorMap[currentDiagonal] = currentDiagonal.LeftSide().GetDirection() + currentDiagonal.RightSide().GetDirection();
				}
			}
			finally{
				iterator.Dispose();
			}
		}
		#endregion
	}
}