﻿using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor (typeof (HeadTracker))]
public class HeadTrackingEditor : Editor 
{
	private SerializedProperty m_ScreenWidth;
	private SerializedProperty m_ScreenHeight;
	private SerializedProperty m_ScreenPosition;
	private SerializedProperty m_ScreenOrientation;
	private SerializedProperty m_RestrictHead;
	private SerializedProperty m_UseCameraClipPlanes;
	private SerializedProperty m_NearClipPlane;
	private SerializedProperty m_FarClipPlane;
	private SerializedProperty m_TrackerAxisScale;
	private SerializedProperty m_TrackerAxisReorderingFormat;
	private SerializedProperty m_ReorderingTrackInputAxis;
	private SerializedProperty m_UseVRPNTracker;
	private SerializedProperty m_VRPNServerURL;
	private SerializedProperty m_VRPNDevieName;
	private SerializedProperty m_VRPNDeviceChannel;

	private GUIContent m_ScreenLabel;
	private GUIContent m_ScreenWidthUI;
	private GUIContent m_ScreenHeightUI;
	private GUIContent m_ScreenPositionUI;
	private GUIContent m_ScreenOrientationUI;
	private GUIContent m_RestrictHeadUI;
	private GUIContent m_ClipPlanesLabel;
	private GUIContent m_UseCameraClipPlanesUI;
	private GUIContent m_NearClipPlaneUI;
	private GUIContent m_FarClipPlaneUI;
	private GUIContent m_TrackerLabel;
	private GUIContent m_TrackerAxisScaleUI;
	private GUIContent m_TrackerAxisReorderingUI;
	private GUIContent[] m_TrackerAxisReorderingSubUI;
	private GUIContent m_ReorderingTrackInputAxisUI;
	private GUIContent m_VRPNLabel;
	private GUIContent m_UseVRPNTrackerUI;
	private GUIContent m_VRPNServerURLUI;
	private GUIContent m_VRPNDevieNameUI;
	private GUIContent m_VRPNDeviceChannelUI;

	public void OnEnable ()
	{
		m_ScreenWidth = serializedObject.FindProperty ("m_ScreenWidth");
		m_ScreenHeight = serializedObject.FindProperty ("m_ScreenHeight");
		m_ScreenPosition = serializedObject.FindProperty ("m_ScreenPosition");
		m_ScreenOrientation = serializedObject.FindProperty ("m_ScreenOrientation");
		m_RestrictHead = serializedObject.FindProperty ("m_RestrictHead");
		m_UseCameraClipPlanes = serializedObject.FindProperty ("m_UseCameraClippingPlanes");
		m_NearClipPlane = serializedObject.FindProperty("m_NearClipPlane");
		m_FarClipPlane = serializedObject.FindProperty ("m_FarClipPlane");
		m_TrackerAxisScale = serializedObject.FindProperty ("m_TrackerAxisScale");
		m_TrackerAxisReorderingFormat = serializedObject.FindProperty ("m_TrackerReorderingFormat");
		m_ReorderingTrackInputAxis = serializedObject.FindProperty ("m_ReorderingTrackInputAxis");
		m_UseVRPNTracker = serializedObject.FindProperty ("m_UseVRPNTracker");
		m_VRPNServerURL = serializedObject.FindProperty ("m_VRPNServerURL");
		m_VRPNDevieName = serializedObject.FindProperty ("m_VRPNDevieName");
		m_VRPNDeviceChannel = serializedObject.FindProperty ("m_VRPNDeviceChannel");

		m_ScreenLabel = new GUIContent ("Virtual Screen Setting", "Property to define the virtual screen");
		m_ScreenWidthUI = new GUIContent ("Width", "Width in Unit");
		m_ScreenHeightUI = new GUIContent ("Height", "Height in Unit");
		m_ScreenPositionUI = new GUIContent ("Position", "Position relative to the parent of the virtual screen");
		m_ScreenOrientationUI= new GUIContent ("Orientation", "Orientation relative to the parent of the virtual screen");
		m_RestrictHeadUI = new GUIContent ("Restrict Head", "Restrict the head position to prevent it to go beyond the screen window. e.g. Z value cannot be negative");

		m_ClipPlanesLabel = new GUIContent ("Clip Planes", "Near and Far clip planes used in calculating frustum");
		m_UseCameraClipPlanesUI = new GUIContent ("Use Camera's Clip Planes", "Use the value of Camera's clip planes");
		m_NearClipPlaneUI = new GUIContent ("Near Clip Planes");
		m_FarClipPlaneUI = new GUIContent ("Far Clip Planes");

		m_TrackerLabel = new GUIContent ("Tracker Setting");
		m_TrackerAxisScaleUI = new GUIContent ("Axis Scale", "Scales the tracker's input data");
		m_TrackerAxisReorderingUI = new GUIContent ("Axis Reordering", "Scales the tracker's input data");
		m_TrackerAxisReorderingSubUI = new GUIContent[3] { new GUIContent ("X"), new GUIContent ("Y"), new GUIContent ("Z") };
		m_ReorderingTrackInputAxisUI = new GUIContent ("Reorder Input Axis", "Reorder the Axis from the input into Unity's axis. put a '-' prefix to indicate a inverted axis value. e.g. '-Y'");

		m_VRPNLabel = new GUIContent ("VRPN Setting");
		m_UseVRPNTrackerUI = new GUIContent ("Use VRPN", "Use VRPN to acquire tracker's input data.");
		m_VRPNServerURLUI = new GUIContent ("Server URL", "VRPN server URL");
		m_VRPNDevieNameUI = new GUIContent ("Devie Name", "VRPN device name");
		m_VRPNDeviceChannelUI = new GUIContent ("Device Channel", "Channel of the device to connect");
	}

	public override void OnInspectorGUI ()
	{
		HeadTracker headTracking = target as HeadTracker;
		serializedObject.Update ();

		EditorGUILayout.LabelField (m_ScreenLabel, EditorStyles.boldLabel);
		EditorGUI.indentLevel++;
		{
			EditorGUILayout.PropertyField (m_ScreenWidth, m_ScreenWidthUI);
			EditorGUILayout.PropertyField (m_ScreenHeight, m_ScreenHeightUI);
			EditorGUILayout.PropertyField (m_ScreenPosition, m_ScreenPositionUI);
			EditorGUILayout.PropertyField (m_ScreenOrientation, m_ScreenOrientationUI);
			EditorGUILayout.PropertyField (m_RestrictHead, m_RestrictHeadUI);
		}
		EditorGUI.indentLevel--;

		EditorGUILayout.LabelField (m_ClipPlanesLabel, EditorStyles.boldLabel);
		EditorGUI.indentLevel++;
		{
			EditorGUILayout.PropertyField (m_UseCameraClipPlanes, m_UseCameraClipPlanesUI);
			EditorGUI.BeginDisabledGroup (m_UseCameraClipPlanes.boolValue);
			{
				if (m_UseCameraClipPlanes.boolValue)
				{
					m_NearClipPlane.floatValue = headTracking.GetComponent<Camera>().nearClipPlane;
					m_FarClipPlane.floatValue = headTracking.GetComponent<Camera>().farClipPlane;
				}
				EditorGUILayout.PropertyField (m_NearClipPlane, m_NearClipPlaneUI);
				EditorGUILayout.PropertyField (m_FarClipPlane, m_FarClipPlaneUI);
			}
			EditorGUI.EndDisabledGroup ();
		}
		EditorGUI.indentLevel--;

		EditorGUILayout.LabelField (m_TrackerLabel, EditorStyles.boldLabel);
		EditorGUI.indentLevel++;
		{
			EditorGUILayout.PropertyField (m_ReorderingTrackInputAxis, m_ReorderingTrackInputAxisUI);
			EditorGUI.BeginDisabledGroup (!m_ReorderingTrackInputAxis.boolValue);
			EditorGUILayout.BeginHorizontal ();
			{
				SerializedProperty cur = m_TrackerAxisReorderingFormat.Copy ();
				cur.NextVisible (true);
				cur.NextVisible (false);

				Rect rect = EditorGUILayout.GetControlRect (true);
 				EditorGUI.MultiPropertyField(rect, m_TrackerAxisReorderingSubUI, cur, m_TrackerAxisReorderingUI);
			}
			EditorGUILayout.EndHorizontal ();
			EditorGUI.EndDisabledGroup ();
			EditorGUILayout.PropertyField (m_TrackerAxisScale, m_TrackerAxisScaleUI);
		}
		EditorGUI.indentLevel--;

		EditorGUILayout.LabelField (m_VRPNLabel, EditorStyles.boldLabel);
		EditorGUI.indentLevel++;
		{
			EditorGUILayout.PropertyField (m_UseVRPNTracker, m_UseVRPNTrackerUI);
			EditorGUI.BeginDisabledGroup (!m_UseVRPNTracker.boolValue);
			{
				EditorGUILayout.PropertyField (m_VRPNServerURL, m_VRPNServerURLUI);
				EditorGUILayout.PropertyField (m_VRPNDevieName, m_VRPNDevieNameUI);
				EditorGUILayout.PropertyField (m_VRPNDeviceChannel, m_VRPNDeviceChannelUI);
			}
			EditorGUI.EndDisabledGroup ();
		}
		EditorGUI.indentLevel--;

		if (headTracking.transform.parent == null)
		{
			EditorGUILayout.HelpBox ("It is recommended to have a parent transform/game object for the camera contains a head tracking component to ensure correct behavior."
				, MessageType.Warning);
		}

		serializedObject.ApplyModifiedProperties ();
	}

	void OnSceneGUI ()
	{
		HeadTracker headTracking = target as HeadTracker;

		// Get the world position of the virtual screen.
		Vector3 screenWorldPosition = Vector3.zero;
		if (headTracking.transform.parent != null)
			screenWorldPosition = headTracking.transform.parent.transform.TransformPoint (headTracking.m_ScreenPosition);

		switch (Tools.current)
		{
			case Tool.Move:
				screenWorldPosition = Handles.PositionHandle (screenWorldPosition, Quaternion.Euler (headTracking.m_ScreenOrientation));
				if (GUI.changed)
				{
					// Get back the local translation for the virtual screen when we save the new position.
					Vector3 pos = headTracking.transform.parent.InverseTransformPoint(screenWorldPosition);
					headTracking.m_ScreenPosition = pos;
				}
				break;
			case Tool.Rotate:
				Quaternion q = Handles.RotationHandle (Quaternion.Euler (headTracking.m_ScreenOrientation), screenWorldPosition);
				if (GUI.changed)
				{
					headTracking.m_ScreenOrientation = q.eulerAngles;
				}
				break;
		}

		// Always recalculate screen matrix and extent in SceneGUI to use the latest setup.
		Matrix4x4 screenMatrix = Matrix4x4.zero;
		headTracking.RecalculateScreenMatrix (ref screenMatrix);
		headTracking.RecalculateExtent ();

		// Draw the virtual screen outline on the scene.
		DrawScreen (screenMatrix, Color.yellow);
	}

	void DrawScreen(Matrix4x4 matrix, Color color)
	{
		HeadTracker headTracking = target as HeadTracker;

		float left = headTracking.m_OriginalLeft;
		float right = headTracking.m_OriginalRight;
		float top = headTracking.m_OriginalTop;
		float bottom = headTracking.m_OriginalBottom;

		Vector3 topLeft = new Vector3 (left, top);
		Vector3 topRight = new Vector3 (right, top);
		Vector3 bottomLeft = new Vector3 (left, bottom);
		Vector3 bottomRight = new Vector3 (right, bottom);

		topLeft = matrix.MultiplyPoint (topLeft);
		topRight = matrix.MultiplyPoint (topRight);
		bottomLeft = matrix.MultiplyPoint (bottomLeft);
		bottomRight = matrix.MultiplyPoint (bottomRight);

		Handles.color = color;
		Handles.DrawLine (topLeft, topRight);
		Handles.DrawLine (topRight, bottomRight);
		Handles.DrawLine (bottomRight, bottomLeft);
		Handles.DrawLine (bottomLeft, topLeft);
	}
}
