﻿using UnityEngine;
using System.Collections;

using Augumenta;

/**
 * PathHandler adds the path motion detection to the
 * game object. It manages the registration of the path
 * detection.
 *
 * OnPath callback is called when hand path occurs for the
 * selected hand path.
 */
public class PathHandler : MonoBehaviour {

	// motion path direction enumerator
	public enum PathDirection { BOTH, POSITIVE, NEGATIVE }

	protected UnityAgapi agapi = UnityAgapi.Instance;

	// selected path
	[Tooltip("Pose to register")]
	public Augumenta.Pose pose = Augumenta.Pose.M001;
	[Tooltip("Pose orientation")]
	public Orientation orientation = Orientation.BOTH;
	[Tooltip("Pose angle range start")]
	public float startAngle = 0f;
	[Tooltip("Pose angle range end")]
	public float endAngle = 0f;
	[Tooltip("Path type to register")]
	public Path path = Path.HORIZONTAL;
	[Tooltip("Path direction to filter events")]
	public PathDirection pathDirection = PathDirection.BOTH;

	[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 path is registered so that they are correctly unregistered
	private bool registered = false;
	// track if path is listened so that there is only only listener set at the a time
	private bool listening = false;

	// registered path
	private Augumenta.Pose _pose;
	private Orientation _orientation;
	private float _startAngle;
	private float _endAngle;
	private Path _path;

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

	public virtual void Start() {
	}

	public virtual void Update() {
		if (pose != _pose ||
			orientation != _orientation ||
			startAngle != _startAngle ||
			endAngle != _endAngle ||
			path != _path) {
			// re-register path if it has been changed
			unregisterPath ();
			registerPath ();
		}
	}

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

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

	protected void registerPath() {
		// cache current registered path for unregister
		_pose = pose;
		_orientation = orientation;
		_startAngle = startAngle;
		_endAngle = endAngle;
		_path = path;
		// 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 path
			registered = agapi.RegisterPath (pose, orientation, startAngle, endAngle, path);
			if (!registered) {
				Debug.LogWarning ("Failed to register path " + pose + ", " + orientation + ", " + path);
				return;
			}
		}

//		Debug.Log("registered path: " + _pose + "(" + _orientation + ") " + _path);

		if (!listening) {
			// add path listener
			agapi.onPathEvent += OnPathListener;
			listening = true;
		}
	}

	protected void unregisterPath() {
		if (listening) {
			// remove path listener
			agapi.onPathEvent -= OnPathListener;
			listening = false;
		}

		if (registered) {
			// unregister path
			agapi.UnregisterPath (_pose, _orientation, _startAngle, _endAngle, _path);
			registered = false;
		}

//		Debug.Log("unregistered path: " + _pose + "(" + _orientation + ") " + _path);
	}

	/**
	 * Check if path event should be handled by this script.
	 */
	protected virtual bool IsEventWanted(PathEvent e) {
		return e.Equals (pose, orientation, startAngle, endAngle, path);
	}

	private void OnPathListener(PathEvent e) {
		// check if the event is the selected one
		if (!IsEventWanted(e)) {
			return;
		}

		// get selected path motion axis value
		float motion = 0;
		switch (path) {
		case Path.HORIZONTAL:
			motion = e.pathMotion [0];
			break;
		case Path.VERTICAL:
			motion = e.pathMotion [1];
			break;
		case Path.DEPTH:
			motion = e.pathMotion [2];
			break;
		}

		// trigger callback if the path direction is the selected one
		if (pathDirection == PathDirection.BOTH
			|| (pathDirection == PathDirection.POSITIVE && motion >= 0)
			|| (pathDirection == PathDirection.NEGATIVE && motion <= 0)) {
			OnPath (e);
		}
	}

	/**
	 * Path event callback
	 */
	public virtual void OnPath(PathEvent e) {
		Debug.Log ("OnPath: " + e);
	}
}
