using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InverseKinematics : MonoBehaviour {
	private Material lineMaterial;
	private Dictionary<GameObject, Transform[]> linePoints = new Dictionary<GameObject, Transform[]>();
	public bool SoftEnabled = true;

	public Transform upperArm;
	public Transform forearm;
	public Transform hand;
	public Transform elbow;
	public Transform target;
	[Space(20)]
	public Vector3 uppperArm_OffsetRotation;
	public Vector3 forearm_OffsetRotation;
	public Vector3 hand_OffsetRotation;
	[Space(20)]
	public bool handMatchesTargetRotation = true;
	[Space(20)]
	public bool linesVisible = true;
	public bool forceLinesInvisible = true;

	float angle;
	float upperArm_Length;
	float forearm_Length;
	float arm_Length;
	float targetDistance;
	float adyacent;

	public void SpawnLines(Color specialColor) {
		lineMaterial = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended Premultiply"));
		NewLine(upperArm, elbow, 0.005f, Color.yellow);
		NewLine(elbow, target, 0.005f, Color.yellow);
		NewLine(upperArm, target, 0.005f, Color.white);
		NewLine(forearm, elbow, 0.01f, specialColor);
	}

	void NewLine(Transform point1, Transform point2, float width, Color color)
	{
		GameObject go = new GameObject("line");
		go.layer = 5;
		go.transform.SetParent(this.transform);
		var lr = go.AddComponent<LineRenderer>();
		lr.startWidth = lr.endWidth = width;
		lr.startColor = lr.endColor = color;
		lr.sharedMaterial = lineMaterial;

		linePoints.Add(go, new Transform[] { point1, point2 });
	}

	void UpdateLines()
    {
		foreach(var kv in linePoints)
        {
			var lr = kv.Key.GetComponent<LineRenderer>();
            if (!SoftEnabled)
            {
				if(lr.enabled != SoftEnabled)
				{
					lr.enabled = SoftEnabled;
				}
			}
            else if((lr.enabled != linesVisible) || (lr.enabled && forceLinesInvisible))
			{
				if(forceLinesInvisible)
                {
					lr.enabled = false;
                }
                else
				{
					lr.enabled = linesVisible;
				}
			}
			for (int i = 0; i < kv.Value.Length; i++)
            {
				lr.SetPosition(i, kv.Value[i].position);
			}
        }
    }

    // Update is called once per frame
    void LateUpdate () {
		if(upperArm != null && forearm != null && hand != null && elbow != null && target != null)
		{
			if (SoftEnabled)
			{
				upperArm_Length = Vector3.Distance(upperArm.position, forearm.position);
				forearm_Length = Vector3.Distance(forearm.position, hand.position);
				arm_Length = upperArm_Length + forearm_Length;
				targetDistance = Vector3.Distance(upperArm.position, target.position);
				targetDistance = Mathf.Min(targetDistance, arm_Length - arm_Length * 0.001f);

				upperArm.LookAt(target, elbow.position - upperArm.position);
				upperArm.Rotate(uppperArm_OffsetRotation);

				Vector3 cross = Vector3.Cross(elbow.position - upperArm.position, forearm.position - upperArm.position);

				adyacent = ((upperArm_Length * upperArm_Length) - (forearm_Length * forearm_Length) + (targetDistance * targetDistance)) / (2 * targetDistance);

				angle = Mathf.Acos(adyacent / upperArm_Length) * Mathf.Rad2Deg;

				if (float.IsNaN(angle))
				{
					return;
				}
				Vector3 oldPosition = upperArm.localPosition;
				upperArm.RotateAround(upperArm.position, cross, -angle);
				upperArm.localPosition = oldPosition;

				forearm.LookAt(target, cross);
				forearm.Rotate(forearm_OffsetRotation);

				if (handMatchesTargetRotation)
				{
					hand.rotation = target.rotation;
					hand.Rotate(hand_OffsetRotation);
				}
			}
            else
            {
				//target.position = hand.position;
				//target.rotation = hand.rotation;
				//target.Rotate(-hand_OffsetRotation);
				//elbow.position = forearm.position;
            }
			UpdateLines();
		}
	}

    private void OnDestroy()
    {
		Destroy(lineMaterial);
		foreach(var i in linePoints)
        {
			Destroy(i.Key);
        }
		Destroy(elbow.gameObject);
		Destroy(target.gameObject);
	}

    void OnDrawGizmos(){
		if (linesVisible) {
			if(upperArm != null && elbow != null && hand != null && target != null && elbow != null){
				Gizmos.color = Color.gray;
				Gizmos.DrawLine (upperArm.position, forearm.position);
				Gizmos.DrawLine (forearm.position, hand.position);
				Gizmos.color = Color.red;
				Gizmos.DrawLine (upperArm.position, target.position);
				Gizmos.color = Color.blue;
				Gizmos.DrawLine (forearm.position, elbow.position);
			}
		}
	}
}
