﻿using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;

namespace RootMotion.Demos {

	/// <summary>
	/// Making a character hold on to a target and swing about it while maintaining his animation.
	/// </summary>
	[RequireComponent(typeof(FullBodyBipedIK))]
	public class PendulumExample : MonoBehaviour {

		[Tooltip("The master weight of this script.")]
		[Range(0f, 1f)] public float weight = 1f;

		[Tooltip("Multiplier for the distance of the root to the target.")]
		public float hangingDistanceMlp = 1.3f;

		[Tooltip("Where does the root of the character land when weight is blended out?")]
		[HideInInspector] public Vector3 rootTargetPosition;

		[Tooltip("How is the root of the character rotated when weight is blended out?")]
		[HideInInspector] public Quaternion rootTargetRotation;

		[SerializeField] Transform target;
		[SerializeField] Transform leftHandTarget;
		[SerializeField] Transform rightHandTarget;
		[SerializeField] Transform leftFootTarget;
		[SerializeField] Transform rightFootTarget;
		[SerializeField] Transform pelvisTarget;
		[SerializeField] Transform bodyTarget;
		[SerializeField] Transform headTarget;
		[SerializeField] Vector3 pelvisDownAxis = Vector3.right;

		private FullBodyBipedIK ik;
		private Quaternion rootRelativeToPelvis;
		private Vector3 pelvisToRoot;
		private float lastWeight;

		void Start() {
			ik = GetComponent<FullBodyBipedIK>();

			// Connect the left hand to the target
			Quaternion targetRotation = target.rotation;
			target.rotation = leftHandTarget.rotation;

			FixedJoint j = target.gameObject.AddComponent<FixedJoint>();
			j.connectedBody = leftHandTarget.GetComponent<Rigidbody>();

			target.GetComponent<Rigidbody>().MoveRotation(targetRotation);
			//target.rotation = targetRotation;

			// Remember the rotation of the root relative to the pelvis
			rootRelativeToPelvis = Quaternion.Inverse(pelvisTarget.rotation) * transform.rotation;

			// Remember the position of the root relative to the pelvis
			pelvisToRoot = Quaternion.Inverse(ik.references.pelvis.rotation) * (transform.position - ik.references.pelvis.position);

			rootTargetPosition = transform.position;
			rootTargetRotation = transform.rotation;

			lastWeight = weight;
		}

		void LateUpdate() {
			// Set effector weights
			if (weight > 0f) {
				ik.solver.leftHandEffector.positionWeight = weight;
				ik.solver.leftHandEffector.rotationWeight = weight;
			} else {
				rootTargetPosition = transform.position;
				rootTargetRotation = transform.rotation;

				if (lastWeight > 0f) {
					ik.solver.leftHandEffector.positionWeight = 0f;
					ik.solver.leftHandEffector.rotationWeight = 0f;
				}
			}

			lastWeight = weight;
			if (weight <= 0f) return;

			// Position the character relative to the ragdoll pelvis
			transform.position = Vector3.Lerp(rootTargetPosition, pelvisTarget.position + pelvisTarget.rotation * pelvisToRoot * hangingDistanceMlp, weight);

			// Rotate the character to the ragdoll pelvis
			transform.rotation = Quaternion.Lerp(rootTargetRotation, pelvisTarget.rotation * rootRelativeToPelvis, weight);

			// Set ik effector positions
			ik.solver.leftHandEffector.position = leftHandTarget.position;
			ik.solver.leftHandEffector.rotation = leftHandTarget.rotation;

			// Get the normal hanging direction
			Vector3 dir = ik.references.pelvis.rotation * pelvisDownAxis;

			// Rotating the limbs
			// Get the rotation from normal hangind direction to the right arm ragdoll direction
			Quaternion rightArmRot = Quaternion.FromToRotation(dir, rightHandTarget.position - headTarget.position);
			// Rotate the right arm by that offset
			ik.references.rightUpperArm.rotation = Quaternion.Lerp(Quaternion.identity, rightArmRot, weight) * ik.references.rightUpperArm.rotation;
			
			Quaternion leftLegRot = Quaternion.FromToRotation(dir, leftFootTarget.position - bodyTarget.position);
			ik.references.leftThigh.rotation = Quaternion.Lerp(Quaternion.identity, leftLegRot, weight) * ik.references.leftThigh.rotation;
			
			Quaternion rightLegRot = Quaternion.FromToRotation(dir, rightFootTarget.position - bodyTarget.position);
			ik.references.rightThigh.rotation = Quaternion.Lerp(Quaternion.identity, rightLegRot, weight) * ik.references.rightThigh.rotation;
		}
	}
}
