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

public class MotionView : MonoBehaviour {
	Pair<float, MotionState> recent;
	public GameObject CoordinateMapperManager;
	CoordinateMapperManager _coordinateMapperManager;
	Body[] body;
	CameraSpacePoint spineMidRecent,
					 handLeftRecent,
					 handRightRecent,
					 kneeLeftBase,
					 kneeRightBase;
	bool IsInitialized;

	// Use this for initialization
	void Start () {
		recent = new Pair<float, MotionState>
		{
			first = 0,
			second = MotionState.UNKNOWN
		};
		
		if(CoordinateMapperManager == null)
			return;
		_coordinateMapperManager
			= CoordinateMapperManager.GetComponent<CoordinateMapperManager>();
		
		body = _coordinateMapperManager.GetBodyBuffer();

		IsInitialized = false;
	}
	
	// Update is called once per frame
	void Update () {
		recent.first += Time.deltaTime;
		recent.second = DetermineState();

        InputManager.Instance.CurrentMotionState = recent.second;
	}

	MotionState DetermineState()
	{
        if (body == null)
		{
			IsInitialized = false;
			return MotionState.UNKNOWN;
		}

		int idx;
		for(idx = 0; idx < body.Length; ++idx)
			if (body[idx] != null && body[idx].IsTracked)
                break;
		if(idx == body.Length || body[idx].Joints == null)
			return MotionState.UNKNOWN;

		if (!IsInitialized)
		{
			kneeLeftBase  = body[idx].Joints[JointType.KneeLeft ].Position;
			kneeRightBase = body[idx].Joints[JointType.KneeRight].Position;
		}
		IsInitialized = true;

        CameraSpacePoint head          = body[idx].Joints[JointType.Head         ]
									 .Position,
                         handLeft      = body[idx].Joints[JointType.HandLeft     ]
						 			 .Position,
                         handRight     = body[idx].Joints[JointType.HandRight    ]
						 			 .Position,
                         spineShoulder = body[idx].Joints[JointType.SpineShoulder]
                                     .Position,
						 spineMid      = body[idx].Joints[JointType.SpineMid     ]
                                     .Position,
						 elbowLeft     = body[idx].Joints[JointType.ElbowLeft    ]
                                     .Position,
						 elbowRight    = body[idx].Joints[JointType.ElbowRight   ]
                                     .Position,
						 kneeLeft      = body[idx].Joints[JointType.KneeLeft     ]
                                     .Position,
						 kneeRight     = body[idx].Joints[JointType.KneeRight    ]
                                     .Position;

        MotionState s = MotionState.UNKNOWN;
        
		// Clap
		if (Distance(handLeft, handRight) > 0.3f
            && spineShoulder.Y < handLeft.Y && spineShoulder.Y < handRight.Y)
            s |= MotionState.CLAP_PREPARE;
        if (Distance(handLeft, handRight) < 0.1f
            && spineShoulder.Y < handLeft.Y && spineShoulder.Y < handRight.Y)
            s |= MotionState.CLAP_DONE;
		
		// Jump
		if (spineMid.Y - spineMidRecent.Y < 0.05f)
			s |= MotionState.JUMP_PREPARE;
		if (spineMid.Y > spineMidRecent.Y)
			s |= MotionState.JUMP_DONE;
		
		// Push Up
		if (head.Y < handLeft.Y && head.Y < handRight.Y)
            s |= MotionState.HURRAY;
		if (handLeft.Y - handLeftRecent.Y > 0)
			s |= MotionState.HAND_MOVE_UP_LEFT;
		if (handLeft.Y - handLeftRecent.Y < 0)
			s |= MotionState.HAND_MOVE_DOWN_LEFT;
		if (handRight.Y - handRightRecent.Y > 0)
			s |= MotionState.HAND_MOVE_UP_RIGHT;
		if (handRight.Y - handRightRecent.Y < 0)
			s |= MotionState.HAND_MOVE_DOWN_RIGHT;
		
		// Guard
		if (handLeft.Y > elbowLeft.Y
		    && Mathf.Abs(elbowLeft.X - spineMid.X) < 0.5f)
			s |= MotionState.GUARD_BASE_LEFT;
		if (handRight.Y > elbowRight.Y
		    && Mathf.Abs(elbowRight.X - spineMid.X) < 0.5f)
			s |= MotionState.GUARD_BASE_RIGHT;
		
		// Hand Up
		if (handLeft.Y - head.Y > 0.2)
			s |= MotionState.HAND_UP_LEFT;
		if (handRight.Y - head.Y > 0.2)
			s |= MotionState.HAND_UP_RIGHT;

		// Hand Down
		if (handLeft.Y < spineMid.Y)
			s |= MotionState.HAND_DOWN_LEFT;
		if (handRight.Y > spineMid.Y)
			s |= MotionState.HAND_DOWN_RIGHT;

		// Jesus
		if (Mathf.Min(handLeft.Y, handRight.Y) > spineShoulder.Y
			&& Mathf.Abs(handLeft.X - head.X) >= 0.5f
			&& Mathf.Abs(handRight.X - head.X) >= 0.5f)
			s |= MotionState.JESUS;

		// Headphone
		if (Mathf.Abs(handLeft.X - head.X) < 0.2f
		    && Mathf.Abs(handLeft.Z - head.Z) < 0.2f)
			s |= MotionState.HEADPHONE_LEFT;
		if (Mathf.Abs(handRight.X - head.X) < 0.2f
		    && Mathf.Abs(handRight.Z - head.Z) < 0.2f)
			s |= MotionState.HEADPHONE_RIGHT;
		
		// On The Table
		if (kneeLeft.Y - kneeLeftBase.Y >= 0.5f
		    || kneeRight.Y - kneeRightBase.Y >= 0.5f)
			s |= MotionState.ON_THE_TABLE;

        InputManager.Instance.Joints = body[idx].Joints;
		
		spineMidRecent  = spineMid;
		handLeftRecent  = handLeft;
		handRightRecent = handRight;

        return s;
	}

	float Distance(CameraSpacePoint a, CameraSpacePoint b)
    {
        return Mathf.Sqrt(Mathf.Pow(a.X - b.X, 2.0f) +
                          Mathf.Pow(a.Y - b.Y, 2.0f) +
                          Mathf.Pow(a.Z - b.Z, 2.0f));
    }

	public Pair<float, MotionState> GetState()
	{
		return recent;
	}
}

[System.Flags]
public enum MotionState : uint
{
    UNKNOWN              = 0x00000,
    CLAP_PREPARE         = 0x00001,
    CLAP_DONE            = 0x00002,
	JUMP_PREPARE         = 0x00004,
	JUMP_DONE            = 0x00008,
    HURRAY               = 0x00010,
	HAND_MOVE_UP_LEFT    = 0x00020,
	HAND_MOVE_DOWN_LEFT  = 0x00040,
	HAND_MOVE_UP_RIGHT   = 0x00080,
	HAND_MOVE_DOWN_RIGHT = 0x00100,
	GUARD_BASE_LEFT      = 0x00200,
	GUARD_BASE_RIGHT     = 0x00400,
	HAND_UP_LEFT         = 0x00800,
	HAND_DOWN_LEFT       = 0x01000,
	HAND_UP_RIGHT        = 0x02000,
	HAND_DOWN_RIGHT      = 0x04000,
	JESUS                = 0x10000,
	HEADPHONE_LEFT       = 0x20000,
	HEADPHONE_RIGHT      = 0x40000,
	ON_THE_TABLE         = 0x80000
}

public class Pair<T1, T2>
{
    public T1 first;
    public T2 second;
}