//
//SpringBone.cs for unity-chan!
//
//Original Script is here:
//ricopin / SpringBone.cs
//Rocket Jump : http://rocketjump.skr.jp/unity3d/109/
//https://twitter.com/ricopin416
//
//Revised by N.Kobayashi 2014/06/20
//
using UnityEngine;
using System.Collections;

namespace UnityChan
{
  public class SpringBone : MonoBehaviour
  {

    //Kobayashi:Thredshold Starting to activate activeRatio
    [Tooltip("Set to lower than manager's dynamicRatio to activate springs")]
    public float threshold = 0.01f;


    [Header("Simulation")]
    //次のボーン
    [Tooltip("Actual 3D point, which position's delta will drive the springs. Usually tip of the hair strand.")]
    public Transform child;
    //ボーンの向き
    public Vector3 boneAxis = new Vector3(-1.0f, 0.0f, 0.0f);
    public Vector3 springForce = new Vector3(0.0f, -0.0001f, 0.0f);

    [Header("Forces")]
    //各SpringBoneに設定されているstiffnessForceとdragForceを使用するか？
    [Tooltip("Override stiffnessForce, dragForce from manager")]
    public bool isUseEachBoneForceSettings = false;

    //バネが戻る力
    [Tooltip("Stiffness: additional force along the boneAxis. 1.0 prohibits movement. 0.0 more movement")]
    public float stiffnessForce = 0.01f;

    //力の減衰力
    [Tooltip("Drag: limit displacement between consequtive frames (prevTipPos - currTipPos). 1.0 slows movement. 0.0 more movement")]
    public float dragForce = 0.4f;

    [Header("Collision")]
    [Tooltip("Draw collision sphere")]
    public bool debug = false;
    [Tooltip("Collision radius")]
    public float radius = 0.05f;
    public SpringCollider[] colliders;

    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
      force += (prevTipPos - currTipPos) * dragForce / sqrDt;

      force += springForce / sqrDt;

      //前フレームと値が同じにならないように
      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++)
      {
        if (Vector3.Distance(currTipPos, colliders[i].transform.position) <= (radius + colliders[i].radius))
        {
          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 OnDrawGizmos()
    {
      if (debug)
      {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(currTipPos, radius);
      }
    }
  }
}