using UnityEngine;
using System.Collections;

namespace UnityChan
{
	public class SpringBone : MonoBehaviour
	{
		//次のボーン
		public Transform child;

		//ボーンの向き
		public Vector3 boneAxis = new Vector3 (-1.0f, 0.0f, 0.0f);
        private float _defaultRadius = 0;
		public float radius = 0.05f;
        public float scale = 1f;

        //各SpringBoneに設定されているstiffnessForceとdragForceを使用するか?
        public bool isUseEachBoneForceSettings = false; 

		//バネが戻る力
		public float stiffnessForce = 0.01f;

		//力の減衰力
		public float dragForce = 0.4f;
		public Vector3 springForce = new Vector3 (0.0f, -0.0001f, 0.0f);
		public SpringCollider[] colliders;
		public bool debug = true;
		//Kobayashi:Thredshold Starting to activate activeRatio
		public float threshold = 0.01f;
		private float springLength;
		private Quaternion localRotation;
		private Transform trs;
		private Vector3 currTipPos;
		private Vector3 prevTipPos;
		//Kobayashi
		private Transform org;
		//Kobayashi:Reference for "SpringManager" component with unitychan 
		private SpringManager managerRef;

		private void Awake ()
		{
			trs = transform;
			localRotation = transform.localRotation;
			//Kobayashi:Reference for "SpringManager" component with unitychan
			// GameObject.Find("unitychan_dynamic").GetComponent<SpringManager>();
			managerRef = GetParentSpringManager (transform);
		}

		private SpringManager GetParentSpringManager (Transform t)
		{
			var springManager = t.GetComponent<SpringManager> ();

			if (springManager != null)
				return springManager;

			if (t.parent != null) {
				return GetParentSpringManager (t.parent);
			}

			return null;
		}

		private void Start ()
		{
			springLength = Vector3.Distance (trs.position, child.position);
			currTipPos = child.position;
			prevTipPos = child.position;
		}

		public void UpdateSpring ()
		{
			//Kobayashi
			org = trs;
			//回転をリセット
			trs.localRotation = Quaternion.identity * localRotation;

            float sqrDt = Time.deltaTime * Time.deltaTime;

			//stiffness
			Vector3 force = trs.rotation * (boneAxis * stiffnessForce) / sqrDt;

            //drag
            //Log("force :x = {0},   y = {1}   z = {2}", force);
            //Log("prevTipPos :x = {0},   y = {1}   z = {2}", prevTipPos);
            //Log("currTipPos :x = {0},   y = {1}   z = {2}", currTipPos);
            force += (prevTipPos - currTipPos)* 2.3f / managerRef.transform.lossyScale.x * dragForce / sqrDt;

			force += springForce / sqrDt;
            //Log("force :x = {0},   y = {1}   z = {2}", force);

            //前フレームと値が同じにならないように
            Vector3 temp = currTipPos;

			//verlet
			currTipPos = (currTipPos - prevTipPos) + currTipPos + (force * sqrDt);

			//長さを元に戻す
			currTipPos = ((currTipPos - trs.position).normalized * springLength) + trs.position;

			//衝突判定
			for (int i = 0; i < colliders.Length; i++) {
                float dis = Vector3.Distance(currTipPos, colliders[i].transform.position);
                float radiusDis = radius + colliders[i].radius;
                if (dis <= radiusDis) {
					Vector3 normal = (currTipPos - colliders [i].transform.position).normalized;
					currTipPos = colliders [i].transform.position + (normal * (radius + colliders [i].radius));
					currTipPos = ((currTipPos - trs.position).normalized * springLength) + trs.position;
				}

			}

			prevTipPos = temp;

			//回転を適用;
			Vector3 aimVector = trs.TransformDirection (boneAxis);
			Quaternion aimRotation = Quaternion.FromToRotation (aimVector, currTipPos - trs.position);
			//original
			//trs.rotation = aimRotation * trs.rotation;
			//Kobayahsi:Lerp with mixWeight
			Quaternion secondaryRotation = aimRotation * trs.rotation;
			trs.rotation = Quaternion.Lerp (org.rotation, secondaryRotation, managerRef.dynamicRatio);
		}

        private void Log(string str, Vector3 vect)
        {
            if (trs.name == "Bone003" && prevTipPos != currTipPos)
            {
                UnityEngine.Debug.LogError(string.Format(str, vect.x, vect.y, vect.z));
                //UnityEngine.Debug.LogError(string.Format("dragForce = {0}   stiness = {1}", dragForce, stiffnessForce));
            }
        }

        public void SetRedius()
        {
            scale = 1;
            if (_defaultRadius == 0)
            {
                _defaultRadius = radius;
            }
            else
            {
                radius = _defaultRadius;
            }
            if (transform.parent)
            {
                SetScale(transform.parent);
            }
            radius = scale * radius;
        }

        private void SetScale(Transform trans)
        {
            scale *= trans.localScale.x;
            if (trans.parent)
            {
                SetScale(trans.parent);
            }
        }

        private void OnDrawGizmos ()
		{
            currTipPos = child.position;
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(currTipPos, radius);
        }
	}
}