#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/*
 * Copyright Scorpius Enterprises
 *
 * Do not distribute
 */
namespace Scorpius_Outfitters
{
    /**
     * This is a helper utility for adding bone parented clothing and accessories to an avatar. Convenient if you, like
     * me, are driven slightly mad dragging and dropping all the bones from your fancy new clothing into your rig.
     *
     * What it does:
     * Given the root of a source and target armature, will parent each bones from the source armature to the first bone
     * encountered in the target armature (in hierarchical order) with an identical name, appending a reference name to
     * the bone from the source armature for later convenience of removal. This version of the script is ONE WAY, it
     * DOES NOT provide a means to remove the newly childed bones automatically. There is a separate script for removing
     * bones parented by this script.
     *
     * How to use:
     * 1. Add the accessory, to be bone parented, to your avatar
     * 2. Add this script to base game object of the accessory (or anywhere really, but it'll use that game object's
     *    name as the default reference name, change as desired)
     * 3. Drag or select the root of the accessory's armature to the "Source Armature" field (it'll likely have a name
     *    like "Something Armature", it will almost certainly have a child like "Hips" or some other body part)
     * 4. Drag or select the root of your avatar's armature to the "Target Armature" field (it'll likely have a name
     *    like "Armature", it will almost certainly have a child called "Hips")
     * 5. Ensure both the avatar and the accessory game objects are unpacked from their prefabs (they should be a
     *    grey/white boxy in the hierarchy, not a blue one - if they are blue, right click them and choose "Unpack
     *    Prefab" - if you're not familiar with prefabs, this means changes made to this game object won't be shared to
     *    others in the same project sharing the prefab; in the case of an avatar this is probably inconsequential,
     *    since you probably only have one instance of the avatar you're working on in your project)
     * 6. Tick the box labeled "Install"
     * 7. Remove the script component if it is within your avatar tree, the VRChat SDK will also provide an Auto Fix
     *    button to do so - optionally, remove the leftover root of the source armature from the accessory game object,
     *    it is not needed
     */
    [ExecuteInEditMode]
    public class AccessoryInstaller : MonoBehaviour
    {
        [SerializeField] private string referenceName;
        [SerializeField] private GameObject sourceArmature;
        [SerializeField] private GameObject targetArmature;

        [Tooltip("One way, only works once")] [SerializeField]
        private bool install;

        private readonly Dictionary<string, Transform> _sourceArmatureDictionary = new Dictionary<string, Transform>();
        private readonly Dictionary<string, Transform> _targetArmatureDictionary = new Dictionary<string, Transform>();

        private void OnValidate()
        {
            if (string.IsNullOrEmpty(referenceName))
                referenceName = gameObject.name;

            transform.SetPositionAndRotation(Vector3.zero, transform.rotation);

            if (!install || !sourceArmature || !targetArmature) return;

            foreach (var child in sourceArmature.GetComponentsInChildren<Transform>())
            {
                _sourceArmatureDictionary.Add(child.name + " (" + referenceName + ")", child);
            }

            foreach (var child in targetArmature.GetComponentsInChildren<Transform>())
            {
                if (!_targetArmatureDictionary.ContainsKey(child.name))
                    _targetArmatureDictionary.Add(child.name, child);
            }

            foreach (var keyValuePair in _sourceArmatureDictionary.Where(keyValuePair =>
                _targetArmatureDictionary.ContainsKey(keyValuePair.Value.name)))
            {
                keyValuePair.Value.SetParent(_targetArmatureDictionary[keyValuePair.Value.name]);
                keyValuePair.Value.name = keyValuePair.Key;
            }
        }
    }
}
#endif