/*
 * +================================================================================+
 *      __  ___  ______________  __  __  ______________   ____
 *     / / / / |/ /  _/_  __/\ \/ / / / / /_  __/  _/ /  / __/
 *    / /_/ /    // /  / /    \  / / /_/ / / / _/ // /___\ \
 *    \____/_/|_/___/ /_/     /_/  \____/ /_/ /___/____/___/
 *
 * +================================================================================+
 *
 *    "THE BEER-WARE LICENSE"
 *    -----------------------
 *
 *    As long as you retain this notice you can do whatever you want with this
 *    stuff. However it is expressly forbidden to sell or commercially distribute
 *    Unity Utils or parts of it apart from your games.
 *    If you meet any of the authors some day, and you think this stuff is
 *    worth it, you can buy them a beer in return.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 * +================================================================================+
 *
 *    Authors:
 *    Michael Müller (mik@mikmueller.ch)
 *
 */

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

public class OnScreenStick : MonoBehaviour {

	/// <summary>
	/// Gets the virtual stick's current axis value.
	/// </summary>
	/// <value>The axis.</value>
	public Vector2 Axis {
		get { return axis/clampRadius; }
	}

	/// <summary>
	/// The axis.
	/// </summary>
	private Vector2 axis;



	/// <summary>
	/// The stick RectTransform for visualization.
	/// </summary>
	private RectTransform stick;

	/// <summary>
	/// The maximum offset from origin.
	/// </summary>
	private float maximumOffset = 25F;

	/// <summary>
	/// The inital position.
	/// </summary>
	private Vector2 iniPos;
	
	[Header("Functionality Settings")]
	/// <summary>
	/// The screen side from which the virtual stick detects touches.
	/// </summary>
	public ScreenSide StickScreenSide;


	public enum ScreenSide	{
		LEFT,
		RIGHT,
		WHOLESCREEN
	}

	/// <summary>
	/// Defines the start and end area in pixels for the actual touch detection.
	/// </summary>
	private int allowedAreaStart, allowedAreaEnd;

	/// <summary>
	/// Wether to place dynamically or not.
	/// </summary>
	public bool DynamicPlacement = true;

	/// <summary>
	/// Resets stick to [0|0] if no touch is detected
	/// </summary>
	public bool ResetOnRelease = true;


	[Header("Appereance Settings")]

	public bool ShowOnlyOnTouch = false;

	/// <summary>
	/// The visible alpha value.
	/// </summary>
	public float VisibleAlphaValue = 1F;

	/// <summary>
	/// The hidden alpha value.
	/// </summary>
	public float HiddenAlphaValue = 0F;


	/// <summary>
	/// The first finger position.
	/// </summary>
	private Vector2 firstFingerPos;

	/// <summary>
	/// The current touch.
	/// </summary>
	private Touch currentTouch;

	/// <summary>
	/// The clamp radius. Should be equivalent to maxOffset value for best visualization results.
	/// </summary>
	private float clampRadius = 25.0F;
	/// <summary>
	/// The wrapper RectTransform.
	/// </summary>
	private RectTransform wrapper;

	/// <summary>
	/// The is visible.
	/// </summary>
	public bool IsVisible {
		get { return isVisible; }
		set { isVisible = value; ChangeVisibility(value); }
	}

	/// <summary>
	/// The is visible.
	/// </summary>
	private bool isVisible = false;

	/// <summary>
	/// The visibles.
	/// </summary>
	private Image[] Visibles;

	// Use this for initialization
	void Awake () {


		SetAllowedScreenSpace (StickScreenSide);
		stick = GetComponent<RectTransform> ();
		wrapper = stick.parent.GetComponent<RectTransform> ();

		//Safe the current iniPos
		iniPos = wrapper.position;

		Visibles = new Image[2];
		Visibles[0] = GetComponent<Image> ();
		Visibles [1] = transform.parent.GetComponent<Image> ();
	}
	
	// Update is called once per frame
	void Update () {

		if (ShowOnlyOnTouch) {
			if(!hasTouch()) {
				if(IsVisible) {
					IsVisible = false;
				}
			} else {
				if(!IsVisible) {
					IsVisible = true;
				}
			}
		}

		if (checkFirstTouch ()) {
			axis = calculateAxis (currentTouch);
		} else {
			if(ResetOnRelease) {
				axis = Vector2.zero;
			}
		}

		//because it gets difficult to compare these two vector formats otherwise.
		if (wrapper.position.ToString() != firstFingerPos.ToString()) {
			wrapper.position = firstFingerPos;
		} else {
			wrapper.position = iniPos;
		}

		//Apply appereance
		SetAppereance (axis);

	}

	bool hasTouch() {
		bool res = false;
		Touch[] Touches = Input.touches;
		foreach (Touch t in Touches) {
			if(t.position.x >= allowedAreaStart && t.position.x <= allowedAreaEnd) {
				res = true;
				return res;
			}
		}

		return res;
	}

	void ChangeVisibility(bool visible) {
		
		if (visible) {
			StopCoroutine("HideRoutine");
			StartCoroutine ("ShowRoutine", 1f);
		} else {
			StopCoroutine("ShowRoutine");
			StartCoroutine ("HideRoutine", 1f);
		}
	}
	
	IEnumerator ShowRoutine(float duration) {
		
		for (float f = 0; f < duration; f += Time.deltaTime) {
			foreach(Image Img in Visibles) {
				SetImageAlpha(Img,Mathf.Lerp(HiddenAlphaValue,VisibleAlphaValue,f));
			}
			yield return null;
		}
	}
	
	
	IEnumerator HideRoutine(float duration) {
		
		for (float f = 0; f < duration; f += Time.deltaTime) {
			foreach(Image Img in Visibles) {
				SetImageAlpha(Img,Mathf.Lerp(VisibleAlphaValue,HiddenAlphaValue,f));
			}
			yield return null;
		}
	}
	
	
	void SetImageAlpha(Image Img, float alphaValue) {
		Color origColor = Img.color;
		origColor.a = alphaValue;
		Img.color = origColor;
	}

	void SetAllowedScreenSpace(ScreenSide screenSide) {
		switch (screenSide) {
		case ScreenSide.LEFT:
			allowedAreaStart = 0;
			allowedAreaEnd = Screen.width/2;
			break;
		case ScreenSide.RIGHT:
			allowedAreaStart = Screen.width/2;
			allowedAreaEnd = Screen.width;
			break;
		case ScreenSide.WHOLESCREEN:
			allowedAreaStart = 0;
			allowedAreaEnd = Screen.width;
			break;
		}
	}

	void SetAppereance(Vector2 input) {
		Vector3 newPos = Vector3.zero;
		newPos.x = axis.x;
		newPos.y = axis.y;

		stick.position = wrapper.position + newPos;
	}


	bool checkFirstTouch() {
		bool res = false;
		Touch[] Touches = Input.touches;
		foreach(Touch t in Touches) {
			if(t.position.x >= allowedAreaStart && t.position.x <= allowedAreaEnd) {
				if(t.phase == TouchPhase.Began) {
					firstFingerPos = t.position;
					res = true;
				} else if(t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary || t.phase == TouchPhase.Ended) {
					currentTouch = t;
					res = true;
				}
			}
		}
		return res;
	}

	Vector2 calculateAxis(Touch t) {
		Vector2 pos = t.position-firstFingerPos;
		pos.x = Mathf.Clamp(pos.x,-clampRadius,clampRadius);
		pos.y = Mathf.Clamp(pos.y,-clampRadius,clampRadius);
		return pos;
	}

}
