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

#if UNITY_EDITOR && FLEXI_MOTION_BOING_KIT

using BoingKit;
using FlexiMotion.Modifiers;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

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

        public const int MenuPriority = 4;

        public const string MenuPrefix = "CONTEXT/" + nameof(FlexiMotion) + "/Boing Kit/";

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

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

        /// <summary>Creates a <see cref="BoingBones"/> component with similar parameters to the `target`.</summary>
        public static BoingBones CopyToBoingKit(FlexiMotion target)
        {
            Undo.RecordObject(target, "Copy to Boing Kit");

            if (!target.TryGetComponent<BoingBones>(out var boing))
                boing = target.gameObject.AddComponent<BoingBones>();

            boing.EnablePositionEffect = false;
            boing.EnableRotationEffect = false;
            boing.EnableScaleEffect = false;
            boing.TwistPropagation = false;

            var boneChains = new List<BoingBones.Chain>();
            CopyBoneChains(target, boneChains);
            boing.BoneChains = boneChains.ToArray();

            var colliderCount = CopyColliders(target, boing);

            EditorUtility.SetDirty(target);

            Debug.Log($"Created {boneChains.Count} Boing Kit Bone Chains and {colliderCount} Colliders.");

            return boing;
        }

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

        private static void CopyBoneChains(FlexiMotion target, List<BoingBones.Chain> chains)
        {
            foreach (var spring in target.Definition.Springs.Transforms)
            {
                if (spring == null)
                    continue;

                var chain = GetOrCreateBoneChainForTransform(chains, spring);
                if (chain == null)
                    continue;

                chain.Root = spring;
            }
        }

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

        private static BoingBones.Chain GetOrCreateBoneChainForTransform(
            List<BoingBones.Chain> chains, Transform transform)
        {
            foreach (var chain in chains)
            {
                if (chain.Root.IsChildOf(transform))
                    return chain;
                else if (transform.IsChildOf(chain.Root))
                    return null;
            }

            var newChain = new BoingBones.Chain();
            newChain.LooseRoot = true;
            newChain.EnableBoingKitCollision = true;
            newChain.EnableUnityCollision = false;
            newChain.EnableInterChainCollision = false;
            chains.Add(newChain);
            return newChain;
        }

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

        private static int CopyColliders(FlexiMotion target, BoingBones boing)
        {
            var boingColliders = new List<BoingBoneCollider>();
            boing.GetComponentsInChildren(boingColliders);
            foreach (var collider in boingColliders)
                Object.DestroyImmediate(collider, true);

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

            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 boingCollider = transform.gameObject.AddComponent<BoingBoneCollider>();
                boingCollider.Shape = BoingBoneCollider.Type.Sphere;
                //boingCollider.Center = sphere.position;
                boingCollider.Radius = sphere.radius;

                boingColliders.Add(boingCollider);
            }

            boing.BoingColliders = boingColliders.ToArray();
            return boingColliders.Count;
        }

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

#endif