using UnityEngine;

namespace pdinklag.CPW.Common
{
    /// <summary>
    /// Deforms a mesh by shifting affected vertices to a retargeted location 
    /// and scaling them according to a sphere of influence.
    /// 
    /// The influence on a vertex depends on its distance from this point.
    /// 
    /// The sphere can be resized to the target body using a diametrical measure (affecting the radius).
    /// Influenced vertices are then pushed or pulled towards the outer sphere's surface accordingly.
    /// </summary>
    public class PropDeformSphere : PropDeform
    {
        [Header("Influence Sphere")]
        [Tooltip("The radius of the inner sphere, which fully influences contained vertices.")]
        public float InnerRadius = 0.1f;

        [Tooltip("The radius of the outer sphere, which influences contained vertices based on their distance from the inner sphere.")]
        public float OuterRadius = 0.2f;

        [Header("Scale Vertices")]
        [Tooltip("The measure used to determine the scaling of an influenced vertex; leave null to disable.")]
        public BodyMeasure Scale;

        [Tooltip("The amount of scaling to apply if any (1 meaning full, 0 meaning none).")]
        [Range(0, 1)]
        public float ScaleAmount = 1;

        // precomputed for the current target
        private Vector3 _shift;
        private float _rAspect;

        private float Influence(Vector3 v, Vector3 pos)
        {
            var dist = Vector3.Distance(v, pos);
            return 1.0f - Mathf.Clamp01((dist - InnerRadius) / (OuterRadius - InnerRadius));
        }

        public override float Influence(Vector3 v) => Influence(v, transform.position);

        public override void PrepareDeform(BodyDescription target)
        {
            _shift = GetComponent<BodyLocation>().Target(target) - transform.position;
            _rAspect = Scale ? Scale.Aspect(target) : 1;
        }

        protected override Vector3 Deform(Vector3 v, BodyDescription target)
        {
            var pos = transform.position;
            var influence = Influence(v, pos);
            if (influence > 0)
            {
                var scale = Scale ? ScaleAmount * (_rAspect - 1.0f) * (v - pos) : Vector3.zero;
                return influence * (_shift + scale);
            }
            else
            {
                return Vector3.zero;
            }
        }

#if UNITY_EDITOR
        public virtual void OnDrawGizmosSelected()
        {
            Gizmos.color = Color.cyan;
            Gizmos.DrawWireSphere(transform.position, InnerRadius);
            Gizmos.color = Color.blue;
            Gizmos.DrawWireSphere(transform.position, OuterRadius);
        }
#endif
    }
}
