using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

namespace Utils.UI {
	public class UISegment : MonoBehaviour {
	  
	  	public int CurrentPoints { 
			get { 
			  return _CurrentPoints;
			}
		  
			set { 
			  _CurrentPoints = value;
			  updateSegments (value);
			}
	  	}

	  [HideInInspector]
	  	public int _CurrentPoints;
	  	public int MaxPoints { get { return TotalSegments; } }
	  	
	  	public int TotalSegments = 8;
	    public float Padding = 0.01F;
	    public float partL { get { return (1F / TotalSegments)-(2*Padding); } }
	   

	  	private Image Mask;
	  [HideInInspector]
	  	public List<Image> Segments;
	  [HideInInspector]
	  	public Transform Container;

	  [System.Serializable]
	  public enum TransitionTypeEnum
	  {
		NONE,
		DROPVERTICAL,
		DROPHORIZONTAL,
		DROPRADIAL,
		FILL
	  }

	  [Header("Transition Settings")]
	  public TransitionTypeEnum TransitionType;
	  public float TransitionDuration;
	  public AnimationCurve TransitionEasing;
	  public float DropDistance = 20F;

	  private bool transitionDone = true;
	  private float transitionTimer = 0F;
	  private float l, e;
	  [HideInInspector]
	  public List<Vector3> OriginalPositions;

	  [Header("Color Settings")]
	  public bool ApplyGradient;
	  public Gradient Colorization;



	  	
	  // Use this for initialization
	  public virtual void Start () {
			Ini ();
	  }




	  /// <summary>
	  /// Initalizes the UISegment with the specified totalSegments and padding.
	  /// </summary>
	  /// <param name="totalSegments">Total segments.</param>
	  /// <param name="padding">Padding.</param>
	  public virtual void Ini(int totalSegments,float padding,bool keepPoints = true) {
		float newL = 1;
		if (keepPoints) {
		  newL = Mathf.InverseLerp (0, TotalSegments, CurrentPoints);
		}


		//Set new Parameters
		TotalSegments = totalSegments;
		Padding = padding;
		Ini ();

		if (keepPoints) {
		  CurrentPoints = Mathf.RoundToInt (TotalSegments * newL);
		  //updateSegmentsOverrideTransition (_CurrentPoints, TransitionTypeEnum.NONE);


		}
	  }


	  /// <summary>
	  /// Clear the UISegment.
	  /// </summary>
	  public void Clear() {
		if (Segments == null || Segments.Count == 0) {
		  return;
		}
		for (int i = 1; i < Segments.Count; i++) {
		  Destroy (Segments [i].gameObject);
		}

		Segments.Clear ();
	  }

	  /// <summary>
	  /// Initalizes the UISegment.
	  /// </summary>
	  public virtual void Ini() {
		Container = this.transform;
		Clear ();
		Segments = new List<Image> ();
		OriginalPositions = new List<Vector3> ();
		_CurrentPoints = TotalSegments;
		GameObject prefab = transform.GetChild (0).gameObject;
		for (int i = 0; i < TotalSegments; i++) {
		  SpawnSegment (prefab, i );
		}

		Destroy (prefab);
	  }


	  public virtual void ApplyColor(GameObject segmentObj, int index) {
			if (ApplyGradient) {
				Image img = segmentObj.GetComponent<Image> ();
				float l = Mathf.InverseLerp (0, TotalSegments-1, index);
				img.color = Colorization.Evaluate (l);
			}
	  }


	  /// <summary>
	  /// Spawns a segment.
	  /// </summary>
	  /// <param name="prefab">Prefab.</param>
	  /// <param name="yRotation">Y rotation.</param>
	  public virtual void SpawnSegment(GameObject prefab,int segmentIndex) {
		GameObject segment = Instantiate (prefab, Container.transform.position, prefab.transform.rotation) as GameObject;
		segment.transform.SetParent (Container, true);
		segment.transform.localScale = Vector3.one;

		Image img = segment.GetComponent<Image> ();
		img.fillAmount = partL;

		ApplyColor (segment, segmentIndex);

		Segments.Add (img);
		OriginalPositions.Add (img.rectTransform.localPosition);
	  }


	  /// <summary>
	  /// Updates the segments.
	  /// </summary>
	  /// <param name="segmentCount">Segment count.</param>
	  private void updateSegments(int segmentCount) {

		switch (TransitionType) {
		case TransitionTypeEnum.NONE:
		  TransitionNone (segmentCount);
		  break;
		case TransitionTypeEnum.DROPVERTICAL:
		  TransitionDropVertical (segmentCount);
		  break;
		case TransitionTypeEnum.DROPHORIZONTAL:
		  TransitionDropHorizontal (segmentCount);
		  break;
		case TransitionTypeEnum.DROPRADIAL:
		  TransitionDropRadial (segmentCount);
		  break;
		case TransitionTypeEnum.FILL:
		  TransitionFill (segmentCount);
		  break;

		}
	  }

	  /// <summary>
	  /// Updates the segments.
	  /// </summary>
	  /// <param name="segmentCount">Segment count.</param>
	  private void updateSegmentsOverrideTransition(int segmentCount,TransitionTypeEnum transition) {

		switch (transition) {
		case TransitionTypeEnum.NONE:
		  TransitionNone (segmentCount);
		  break;
		case TransitionTypeEnum.DROPVERTICAL:
		  TransitionDropVertical (segmentCount);
		  break;
		case TransitionTypeEnum.DROPHORIZONTAL:
		  TransitionDropHorizontal (segmentCount);
		  break;
		case TransitionTypeEnum.DROPRADIAL:
		  TransitionDropRadial (segmentCount);
		  break;
		case TransitionTypeEnum.FILL:
		  TransitionFill (segmentCount);
		  break;

		}
	  }


	  public virtual void TransitionNone(int segmentCount) {
		for (int i = 0; i < Segments.Count; i++) {
		  Segments [i].enabled = false;

		  if (i <= segmentCount-1) {
			Segments [i].enabled = true;
		  }
		}
	  }

	  public virtual void TransitionFill(int segmentCount) {
		for (int i = 0; i < Segments.Count; i++) {

		  if (i > segmentCount-1 && Segments [i].enabled) {
			//Drop em off
			StartCoroutine(FillRoutine(i,false));
		  }



		  if (i <= segmentCount-1 && !Segments [i].enabled) {
			//Get them in
			StartCoroutine(FillRoutine(i,true));
		  }
		}
	  }
		
	  public virtual void TransitionDropVertical(int segmentCount) {
		for (int i = 0; i < Segments.Count; i++) {

		  if (i > segmentCount-1 && Segments [i].enabled) {
			//Drop em off
			StartCoroutine(DropOffRoutine(i,new Vector3(0,1,0),DropDistance));
		  }



		  if (i <= segmentCount-1 && !Segments [i].enabled) {
			//Get them in
			StartCoroutine(GetInRoutine(i,new Vector3(0,1,0),DropDistance));
		  }
		}
	  }

	  public virtual void TransitionDropHorizontal(int segmentCount) {
		for (int i = 0; i < Segments.Count; i++) {

		  if (i > segmentCount-1 && Segments [i].enabled) {
			//Drop em off
			StartCoroutine(DropOffRoutine(i,new Vector3(1,0,0),DropDistance));
		  }



		  if (i <= segmentCount-1 && !Segments [i].enabled) {
			//Get them in
			StartCoroutine(GetInRoutine(i,new Vector3(1,0,0),DropDistance));
		  }
		}
	  }

	  public virtual void TransitionDropRadial(int segmentCount) {
		
		for (int i = 0; i < Segments.Count; i++) {
		 
		  if (i > segmentCount-1 && Segments [i].enabled) {
			//Drop em off
			//StartCoroutine(DropOffRoutine(Segments[i],dir,1f));
			StartCoroutine (ScaleRoutine (i, DropDistance));
		  }



		  if (i <= segmentCount-1 && !Segments [i].enabled) {
			//Get them in
			//StartCoroutine(GetInRoutine(Segments[i],dir,1f));
			StartCoroutine (ScaleRoutine (i, 1f));
		  }
		}
	  }


	  public IEnumerator FillRoutine(int segmentIndex,bool fillUp) {
		Color targetCol;
		Color originalCol;



		float targetFill;
		float originalFill;

		if (fillUp) {
		  Segments [segmentIndex].enabled = true;
		  targetFill = partL;
		  originalFill = 0;

		  originalCol = Segments [segmentIndex].color;
		  targetCol = originalCol;
		  targetCol.a = 1;
		} else {
		  targetFill = 0;
		  originalFill = partL;

		  originalCol = Segments [segmentIndex].color;
		  targetCol = originalCol;
		  targetCol.a = 0;
		}

		float l, e;
		for (float f = 0; f < TransitionDuration; f += Time.deltaTime) {
		  l = Mathf.InverseLerp (0, TransitionDuration, f);
		  e = Utils.Ease.GetEase (TransitionEasing,l);

		  Segments [segmentIndex].fillAmount = Mathf.Lerp (originalFill, targetFill, e);
		  Segments[segmentIndex].color = Color.Lerp (originalCol, targetCol, e);

		  yield return null;
		}

		Segments [segmentIndex].fillAmount = targetFill;
		Segments[segmentIndex].color = targetCol;

		if (!fillUp) {
		  Segments [segmentIndex].enabled = false;
		}

	  }


	  public IEnumerator DropOffRoutine(int segmentIndex,Vector3 dir,float distance) {
		Color targetCol = Segments[segmentIndex].color;
		targetCol.a = 0;
		Color originalCol = Segments[segmentIndex].color;

		Vector3 targetPos = Segments[segmentIndex].rectTransform.localPosition + dir * distance;
		Vector3 originalPos = OriginalPositions [segmentIndex];

		float l, e;
		for (float f = 0; f < TransitionDuration; f += Time.deltaTime) {
		  l = Mathf.InverseLerp (0, TransitionDuration, f);
		  e = Utils.Ease.GetEase (TransitionEasing,l);

		  Segments[segmentIndex].rectTransform.localPosition = Vector3.Lerp (originalPos, targetPos,e);
		  Segments[segmentIndex].color = Color.Lerp (originalCol, targetCol, e);

		  yield return null;
		}

		Segments[segmentIndex].rectTransform.localPosition = targetPos;
		Segments[segmentIndex].color = targetCol;
		Segments[segmentIndex].enabled = false;

	  }

	  private IEnumerator GetInRoutine(int segmentIndex,Vector3 dir,float distance) {
		Segments[segmentIndex].enabled = true;
		Color originalCol = Segments[segmentIndex].color;
		Color targetCol = originalCol;
		targetCol.a = 1;


		Vector3 targetPos = OriginalPositions [segmentIndex];
		Vector3 originalPos = Segments[segmentIndex].rectTransform.localPosition;

		float l, e;
		for (float f = 0; f < TransitionDuration; f += Time.deltaTime) {
		  l = Mathf.InverseLerp (0, TransitionDuration, f);
		  e = Utils.Ease.GetEase (TransitionEasing,l);

		  Segments[segmentIndex].rectTransform.localPosition = Vector3.Lerp (originalPos, targetPos,e);
		  Segments[segmentIndex].color = Color.Lerp (originalCol, targetCol, e);

		  yield return null;
		}


		Segments[segmentIndex].rectTransform.localPosition = targetPos;
		Segments[segmentIndex].color = targetCol;
		Segments[segmentIndex].enabled = true;

	  }



	  private IEnumerator ScaleRoutine(int segmentIndex,float distance) {
		Color originalCol = Segments[segmentIndex].color;
		Color targetCol = originalCol;

		if (distance == 1) {
		  Segments[segmentIndex].enabled = true;
		  targetCol.a = 1;
		} else {
		  targetCol.a = 0;
		}

	  Vector3 originalScale = Vector3.one;
	  Vector3 targetScale;
	  if (distance > 0) {
		originalScale = Vector3.one;
		targetScale = new Vector3 (distance, distance, distance);
	  } else {
		targetScale = Vector3.one;
		originalScale = new Vector3 (distance, distance, distance);
	  }
		float l, e;
		for (float f = 0; f < TransitionDuration; f += Time.deltaTime) {
		  l = Mathf.InverseLerp (0, TransitionDuration, f);
		  e = Utils.Ease.GetEase (TransitionEasing,l);

		  Segments[segmentIndex].rectTransform.localScale = Vector3.Lerp (originalScale, targetScale, e);
		  Segments[segmentIndex].color = Color.Lerp (originalCol, targetCol, e);

		  yield return null;
		}


		if (distance != 1) {
		  Segments[segmentIndex].enabled = false;
		}

	  }



	 


	  /// <summary>
	  /// Adds a point.
	  /// </summary>
	  public void AddPoint() {
		CurrentPoints++;
		if (CurrentPoints > TotalSegments) {
		  CurrentPoints = TotalSegments;
		}
	  }


	  /// <summary>
	  /// Removes a point.
	  /// </summary>
	  public void RemovePoint() {
		CurrentPoints--;
		if (CurrentPoints < 0) {
		  CurrentPoints = 0;
		}
	  }

	  /// <summary>
	  /// Adds 1 to the segment count.
	  /// </summary>
	  public void AddSegmentCount() {
		SetSegmentCount (TotalSegments + 1);
	  }

	  /// <summary>
	  /// Reduces the segment count by 1.
	  /// </summary>
	  public void ReduceSegmentCount() {
		if (TotalSegments - 1 > 0) {
		  SetSegmentCount (TotalSegments - 1);
		}
	  }

	  /// <summary>
	  /// Sets the segment count.
	  /// </summary>
	  /// <param name="count">Count.</param>
	  public void SetSegmentCount(int count) {
		Ini (count, Padding);
	  }


	  /// <summary>
	  /// Sets the points.
	  /// </summary>
	  /// <param name="newPoints">New points.</param>
	  public void SetPoints(int newPoints) {

		if (newPoints <= 0) {
		  CurrentPoints = 0;
		} else if (newPoints >= TotalSegments) {
		  CurrentPoints = TotalSegments;
		} else {
		  CurrentPoints = newPoints;
		}
	  }


	  #region Demo

	  /// <summary>
	  /// Sets the segment count from input field "SegmentSet".
	  /// </summary>
	  public void SetSegmentCountFromInputField() {
		int newSegmentCount = int.Parse (GameObject.Find("SegmentSet").GetComponent<InputField> ().text);
		SetSegmentCount (newSegmentCount);
	  }

	  /// <summary>
	  /// Sets the points from input field "PointSet".
	  /// </summary>
	  public void SetPointsFromInputField() {
		int newPoints = int.Parse (GameObject.Find("PointSet").GetComponent<InputField> ().text);
		SetPoints (newPoints);
	  }

	  #endregion

	}

}