﻿using UnityEngine;
using System.Collections;

using Augumenta;

/**
 * TransitionHandler adds the hand transition detection to
 * game object. It manages the registration of the transition
 * detection.
 *
 * OnTransition callback is called when hand transition occurs
 * for the selected hand transition.
 */
public class TransitionHandler : MonoBehaviour {

	protected UnityAgapi agapi = UnityAgapi.Instance;
	[Tooltip("The origin for the detection when using absolute coordinates as GameObject, when needed")]
	public GameObject headReferential = null;
	// selected transition
	[Tooltip("Transition start pose")]
	public Augumenta.Pose fromPose = Augumenta.Pose.P001;
	[Tooltip("Transition start pose orientation")]
	public Orientation fromOrientation = Orientation.BOTH;
	[Tooltip("Transition start pose angle range start")]
	public float fromStartAngle = 0f;
	[Tooltip("Transition start pose angle range end")]
	public float fromEndAngle = 0f;
	[Tooltip("Transition end pose")]
	public Augumenta.Pose toPose = Augumenta.Pose.P032;
	[Tooltip("Transition end pose orientation")]
	public Orientation toOrientation = Orientation.BOTH;
	[Tooltip("Transition end pose angle range start")]
	public float toStartAngle = 0f;
	[Tooltip("Transition end pose angle range end")]
	public float toEndAngle = 0f;

	[Tooltip("Register pose even if the object is disabled")]
	public bool registerAlways = false;

	[Tooltip("Attach pose listeners only, don't register for poses")]
	public bool listenersOnly = false;
	// track if transition is registered so that they are correctly unregistered
	private bool registered = false;
	// track if transition is listened so that there is only only listener set at the a time
	private bool listening = false;

	// registered transition
	private Augumenta.Pose _fromPose;
	private Orientation _fromOrientation;
	private float _fromStartAngle;
	private float _fromEndAngle;
	private Augumenta.Pose _toPose;
	private Orientation _toOrientation;
	private float _toStartAngle;
	private float _toEndAngle;
	protected GameObject referential = null;

	public virtual void Awake() {
		// register pose when script is loaded if the 'registerAlways' is set
		if (registerAlways) {
			registerTransition ();
		}
	}

	public virtual void Start() {
	}

	public virtual void OnDestroy() {
		// make sure that everything is unregistered when object is destroyed
		unregisterTransition ();
	}

	public virtual void OnEnable() {
		if (!registerAlways) {
			registerTransition ();
		}
	}

	public virtual void OnDisable() {
		if (!registerAlways) {
			unregisterTransition ();
		}
	}

	public virtual void Update() {
		if (fromPose != _fromPose ||
			fromOrientation != _fromOrientation ||
			fromStartAngle != _fromStartAngle ||
			fromEndAngle != _fromEndAngle ||
			toPose != _toPose ||
			toOrientation != _toOrientation ||
			toStartAngle != _toStartAngle ||
			toEndAngle != _toEndAngle) {
			// re-register transition if it has been changed
			unregisterTransition ();
			registerTransition ();
		}
	}

	protected void registerTransition() {
		// cache current registered transition for unregister
		_fromPose = fromPose;
		_fromOrientation = fromOrientation;
		_fromStartAngle = fromStartAngle;
		_fromEndAngle = fromEndAngle;
		_toPose = toPose;
		_toOrientation = toOrientation;
		_toStartAngle = toStartAngle;
		_toEndAngle = toEndAngle;
		// note that these are stored before trying to register, because
		// if registering fails then in the next update call it won't try to register.

		if (!listenersOnly) {
			// register transition
			registered = agapi.RegisterTransition (fromPose, fromOrientation, fromStartAngle, fromEndAngle, toPose, toOrientation, toStartAngle, toEndAngle);
			if (!registered) {
				Debug.LogWarning ("Failed to register transition: " + fromPose + "(" + fromOrientation + ") -> " + toPose + "(" + toOrientation + ")");
				return;
			}
		}
//		Debug.Log("registered transition: " + _fromPose + "(" + _fromOrientation + ") -> " + _toPose + "(" + _toOrientation + ")");

		if (!listening) {
			// add transition listener
			agapi.onTransitionEvent += OnTransitionListener;
			listening = true;
		}
	}

	protected void unregisterTransition() {
		if (listening) {
			// remove transition listener
			agapi.onTransitionEvent -= OnTransitionListener;
			listening = false;
		}

		if (registered) {
			// unregister transition
			agapi.UnregisterTransition (_fromPose, _fromOrientation, _fromStartAngle, _fromEndAngle, _toPose, _toOrientation, _toStartAngle, _toEndAngle);
			registered = false;
		}

//		Debug.Log("unregistered transition: " + _fromPose + "(" + _fromOrientation + ") -> " + _toPose + "(" + _toOrientation + ")");
	}

	/**
	 * Check if transition event should be handled by this script.
	 */
	protected virtual bool IsEventWanted(TransitionEvent e) {
		return e.Equals (fromPose, fromOrientation, fromStartAngle, fromEndAngle, toPose, toOrientation, toStartAngle, toEndAngle);
	}

	private void OnTransitionListener(TransitionEvent e) {
		// check if the event is the selected one
		if (!IsEventWanted(e)) {
			return;
		}
		var headPosition=transform.forward;
		var headRotation=new Quaternion(0,0,0,1); // no rotation
		// if headReferential is null, use Camera.main. If that doesn't exists, use no rotation
		if (headReferential != null) {
			headPosition = headReferential.transform.position;
			headRotation = headReferential.transform.rotation;
			referential = headReferential;
			Debug.Log ("headReferential!=null: \n" + headPosition + headRotation);
		} else if (Camera.main != null) {
			headPosition = Camera.main.transform.position;
			headRotation = Camera.main.transform.rotation;
			referential = Camera.main.gameObject;
			Debug.Log ("Camera.main != null: \n" + headPosition + headRotation);
		} else {
			Debug.LogWarning ("headReferential is missing and there is no Camera.main!");
		}
		var position = headRotation*(headPosition+e.poseIn.GetPosition()); // get the new vector
		var rotation = headRotation*e.poseIn.GetRotation();
		// trigger transition callback
		OnTransition (e, position, rotation);
	}

	/**
	 * Transition event callback
	 */
	public virtual void OnTransition(TransitionEvent e, Vector3 position, Quaternion rotation) {
		Debug.Log ("OnTransition: " + e);
	}
}
