using UnityEngine;
using UnityEngine.UI;

public class InputManager : MonoBehaviour {

	Rigidbody grabbedRigidbody;
	float grabbedRigidbodyOriginalAngularDrag;
	float distanceFromCamera;

	public enum JointToUse
	{
		SpringJoint,
		FixedJoint
	}
	public JointToUse jointToUse;

	Joint joint;
	Rigidbody cursorRigidbody;
//	Quaternion cursorRigidbodyInitialRotation;

	public LayerMask layersForRaycast;

	public CameraController cameraController;

	public bool showCursorRigidbody;
	public float cursorViewRadius;
	public Vector2 allowedDistanceFromCameraRange;
	public float distanceChangeSpeed;
	public bool dontBreakConnectionUponRelease;

	public float cursorJointSpring;
	public float cursorJointDamper;
	public bool cursorJointEnablePreprocessing;

	public float grabbedRigidbodyAngularDrag;

	public bool useGyroInput;
	Quaternion lastGyroAttitude;
	public Text gyroDebugText;

	void Awake()
	{
		var cursorGO = GameObject.CreatePrimitive(PrimitiveType.Sphere);
		cursorGO.name = "CursorRigidbody";
		cursorGO.transform.localScale = Vector3.one * cursorViewRadius;
		cursorRigidbody = cursorGO.AddComponent<Rigidbody>();
		cursorRigidbody.isKinematic = true;
		if(jointToUse == JointToUse.FixedJoint)
		{
			joint = cursorRigidbody.gameObject.AddComponent<FixedJoint>();
		}
		else if ( jointToUse == JointToUse.SpringJoint)
		{
			joint = cursorRigidbody.gameObject.AddComponent<SpringJoint>();
		}

		Input.gyro.enabled = true;
		Input.gyro.updateInterval = 0.01f;
	}

	void Update () {
		cameraController.allowMovement = grabbedRigidbody == null;

		if(jointToUse == JointToUse.SpringJoint)
		{
			var springJoint = joint as SpringJoint;
			springJoint.spring = cursorJointSpring;
			springJoint.damper = cursorJointDamper;
		}
		joint.enablePreprocessing = cursorJointEnablePreprocessing;

		if(Input.GetMouseButtonDown(0))
		{
			var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			RaycastHit hit;
			if(Physics.Raycast(ray, out hit, float.MaxValue, layersForRaycast.value))
			{
				var rb = hit.collider.GetComponent<Rigidbody>();
				if(rb != grabbedRigidbody && rb != cursorRigidbody)
				{
					if(grabbedRigidbody != null)
					{
						grabbedRigidbody.angularDrag = grabbedRigidbodyOriginalAngularDrag;
					}
					grabbedRigidbody = rb;
					grabbedRigidbodyOriginalAngularDrag = grabbedRigidbody.angularDrag;
					grabbedRigidbody.angularDrag = grabbedRigidbodyAngularDrag;
					cursorRigidbody.transform.position = hit.point;
					cursorRigidbody.transform.localRotation = grabbedRigidbody.rotation;
//					cursorRigidbodyInitialRotation = cursorRigidbody.rotation;
					joint.connectedBody = grabbedRigidbody;
					joint.anchor = cursorRigidbody.transform.InverseTransformPoint(hit.point);

					distanceFromCamera = Vector3.Distance(grabbedRigidbody.position, Camera.main.transform.position);
				}
			}
		}
		if(Input.GetMouseButton(0) && grabbedRigidbody != null)
		{
			var newPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distanceFromCamera));
			cursorRigidbody.MovePosition(newPos);
		}
		if(Input.GetMouseButtonUp(0) && grabbedRigidbody != null && !dontBreakConnectionUponRelease)
		{
			joint.connectedBody.WakeUp();
			joint.connectedBody = null;
			grabbedRigidbody.angularDrag = grabbedRigidbodyOriginalAngularDrag;
			grabbedRigidbody = null;
		}

		distanceFromCamera = Mathf.Clamp(distanceFromCamera + Input.mouseScrollDelta.y * Time.deltaTime * distanceChangeSpeed,
			allowedDistanceFromCameraRange.x,
			allowedDistanceFromCameraRange.y);

		cursorRigidbody.GetComponent<MeshRenderer>().enabled = showCursorRigidbody && grabbedRigidbody != null;

		if(useGyroInput && grabbedRigidbody != null)
		{
			var attitude = Input.gyro.attitude;
			var tempZ = attitude.z;
			attitude.z = attitude.y;
			attitude.y = tempZ;
			var qTransition = attitude * Quaternion.Inverse(lastGyroAttitude);
//			var qTransition = Quaternion.RotateTowards(lastGyroAttitude, attitude, 5f);
			cursorRigidbody.MoveRotation(cursorRigidbody.rotation * qTransition);
			lastGyroAttitude = attitude;
		}

		if(Input.GetKeyDown(KeyCode.G))
		{
			useGyroInput = !useGyroInput;
		}
//		var attitudeEuler = Input.gyro.attitude.eulerAngles;
//		var acc = Input.gyro.userAcceleration;
//		gyroDebugText.text = string.Format("Attitude: {0},{1},{2}\nUser Acceleration:{3},{4},{5}", attitudeEuler.x, attitudeEuler.y, attitudeEuler.z, acc.x, acc.y, acc.z);
	}
}
