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

namespace Arugula.Prototypical
{
    public class RigidbodyDrag : MonoBehaviour
    {
        public static event Action<RigidbodyDrag, Rigidbody, Vector3> OnDragStart = delegate { };
        public static event Action<RigidbodyDrag, Rigidbody, Vector3> OnDrag = delegate { };
        public static event Action<RigidbodyDrag, Rigidbody, Vector3> OnDragEnd = delegate { };

        [Header("Physics")]
        public float spring = 10f;
        public float damper = 0.2f;
        public float minDistance;
        public float maxDistance;
        public float tolerance = 0.025f;
        public float breakForce = float.PositiveInfinity;
        public float breakTorque = float.PositiveInfinity;

        public bool IsDragging => rb != null;
        protected SpringJoint joint;
        protected Vector3 position;
        protected Rigidbody rb;

        protected virtual void StartDragging(Rigidbody rb, Vector3 pos)
        {
            if (joint)
                return;

            joint = CreateJoint(rb, pos);
            OnDragStart(this, rb, pos);
        }

        protected virtual void Drag(Vector3 position)
        {
            if (!joint)
                return;

            this.position = position;
            joint.connectedAnchor = position;
        }

        protected virtual void StopDragging()
        {
            if (!joint)
                return;

            OnDragEnd(this, joint.GetComponent<Rigidbody>(), position);
            rb = null;
            Destroy(joint);
        }

        private void FixedUpdate()
        {
            if (IsDragging)
                rb.WakeUp();
        }

        SpringJoint CreateJoint(Rigidbody rb, Vector3 pos)
        {
            this.rb = rb;
            RigidbodyDragOptions opts = rb.GetComponent<RigidbodyDragOptions>();

            SpringJoint j = rb.gameObject.AddComponent<SpringJoint>();
            j.autoConfigureConnectedAnchor = false;

            j.anchor = (opts && opts.useCenterOfMass) ? rb.centerOfMass : rb.transform.InverseTransformPoint(pos);

            if (opts)
            {
                j.spring = opts.spring;
                j.damper = opts.damper;
                j.minDistance = opts.minDistance;
                j.maxDistance = opts.maxDistance;
                j.tolerance = opts.tolerance;
                j.breakForce = opts.breakForce;
                j.breakTorque = opts.breakTorque;
            }
            else
            {
                j.spring = spring;
                j.damper = damper;
                j.minDistance = minDistance;
                j.maxDistance = maxDistance;
                j.tolerance = tolerance;
                j.breakForce = breakForce;
                j.breakTorque = breakTorque;
            }

            return j;
        }
    }
}