﻿// FlexiMotion // https://kybernetik.com.au/flexi-motion // Copyright 2023 Kybernetik //

#if UNITY_EDITOR && FLEXI_MOTION_TAIL_ANIMATOR

using FlexiMotion.Modifiers;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
using TailAnimator = FIMSpace.FTail.TailAnimator2;

namespace FlexiMotion.Editor
{
    /// <summary>[Editor-Only]
    /// Utilities for converting between <see cref="FlexiMotion"/> and <see cref="TailAnimator"/>.
    /// </summary>
    /// <example><see href="https://kybernetik.com.au/flexi-motion/docs/samples/benchmark">Benchmark</see></example>
    public static class FlexiMotionToTailAnimator
    {
        /************************************************************************************************************************/

        public const int MenuPriority = 5;

        public const string MenuPrefix = "CONTEXT/" + nameof(FlexiMotion) + "/Tail Animator/";

        /************************************************************************************************************************/

        [MenuItem(MenuPrefix + "Copy to Tail Animator", priority = MenuPriority)]
        private static void CopyToTailAnimator(MenuCommand command)
            => CopyToTailAnimator((FlexiMotion)command.context);

        /// <summary>Creates <see cref="TailAnimator"/> components with similar parameters to the `target`.</summary>
        public static List<TailAnimator> CopyToTailAnimator(FlexiMotion target)
        {
            Undo.RecordObject(target, "Copy to Tail Animator");

            var tailAnimators = new List<TailAnimator>();
            target.GetComponents(tailAnimators);
            foreach (var tailAnimator in tailAnimators)
                Object.DestroyImmediate(tailAnimator, true);

            tailAnimators.Clear();

            CopyIndividualRootsAsTailAnimators(target, tailAnimators);

            var colliderCount = CopyColliders(target, tailAnimators);

            EditorUtility.SetDirty(target);

            Debug.Log($"Created {tailAnimators.Count} Dynamic Bones and {colliderCount} Colliders.");

            return tailAnimators;
        }

        /************************************************************************************************************************/

        private static void CopyIndividualRootsAsTailAnimators(FlexiMotion target, List<TailAnimator> tailAnimators)
        {
            var springCount = target.Definition.Springs.Count;
            for (int i = 0; i < springCount; i++)
            {
                var transform = target.Definition.Springs.Transforms[i];
                if (transform == null)
                    continue;

                var tailAnimator = GetOrCreateTailAnimatorForTransform(target.gameObject, tailAnimators, transform);
                if (tailAnimator == null)
                    continue;

                tailAnimator.UpdateRate = (int)target.Definition.UpdatesPerSecond;
                tailAnimator.DeltaType = TailAnimator.EFDeltaType.DeltaTime;
                tailAnimator.StartBone = transform;
                tailAnimator.Springiness = 1;// 1 - drag;
                tailAnimator.Slithery = 1;// 1 - stiffness;
                tailAnimator.Curling = 1;

                tailAnimator.UseCollision = true;
                tailAnimator.CollisionSpace = TailAnimator.ECollisionSpace.Selective_Fast;
                tailAnimator.CollideWithDisabledColliders = true;
                tailAnimator.CollideWithOtherTails = false;
                tailAnimator.CollidersScaleMul = target.Definition.Springs.Radii[i];
            }
        }

        /************************************************************************************************************************/

        private static TailAnimator GetOrCreateTailAnimatorForTransform(
            GameObject gameObject, List<TailAnimator> tailAnimators, Transform transform)
        {
            foreach (var tailAnimator in tailAnimators)
            {
                if (tailAnimator.StartBone.IsChildOf(transform))
                    return tailAnimator;
                else if (transform.IsChildOf(tailAnimator.StartBone))
                    return null;
            }

            var newTailAnimator = gameObject.AddComponent<TailAnimator>();
            if (newTailAnimator.IncludedColliders == null)
                newTailAnimator.IncludedColliders = new List<Collider>();
            tailAnimators.Add(newTailAnimator);
            return newTailAnimator;
        }

        /************************************************************************************************************************/

        private static int CopyColliders(FlexiMotion target, List<TailAnimator> tailAnimators)
        {
            foreach (var collider in target.GetComponentsInChildren<Collider>())
                Object.DestroyImmediate(collider, true);

            var colliderCount = 0;
            if (!target.TryGetComponent<SphereCollidersModifier>(out var modifier))
                return colliderCount;

            var count = modifier.Colliders.Count;
            for (int i = 0; i < count; i++)
            {
                if (!modifier.Colliders.TryGet(i, out var transform, out var sphere))
                    continue;

                var tailAnimatorCollider = transform.gameObject.AddComponent<SphereCollider>();
                tailAnimatorCollider.enabled = false;
                tailAnimatorCollider.center = sphere.position;
                tailAnimatorCollider.radius = sphere.radius;
                colliderCount++;

                foreach (var tailAnimator in tailAnimators)
                    tailAnimator.IncludedColliders.Add(tailAnimatorCollider);
            }

            return colliderCount;
        }

        /************************************************************************************************************************/
    }
}

#endif