﻿#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public static class ColliderHelper
{
    [MenuItem("GameObject/Auto-Attach Colliders", false, 10)]
    public static void ContextAutoCollider(MenuCommand command)
    {
        var context = command.context as GameObject;

        if (context == null || EditorUtility.IsPersistent(context))
        {
            Debug.LogError("No object selected. Right-click on a non-collider gameobject in the scene and try again.");
            return;
        }

        var colliderName = GetColliderName(context.name);
        Debug.Log($"ColliderHelper - ContextAutoCollider - colliderName is {colliderName}");

        if (context.name == colliderName)
        {
            Debug.LogError($"Right-click on the non-collider gameobject and try again.");
            return;
        }

        ConnectAutoColliderTree(context);
    }

    public static void ConnectAutoColliderTree(GameObject current)
    {
        Debug.Log($"ColliderHelper - ConnectAutoColliderTree - starting on {current}");
        var children = new List<GameObject>();

        for (var i = 0; i < current.transform.childCount; i++)
        {
            var t = current.transform.GetChild(i);
            var child = t.gameObject;
            children.Add(child);
        }

        var dual = FindColliderDual(current);

        if (dual != null)
        {
            ConnectColliderNode(current, dual);
        }
        else
        {
            Debug.LogWarning($"ColliderHelper - ConnectAutoColliderTree - missing dual for collider: \"{current.name}\". Descending down the tree.");
        }

        foreach (var child in children)
        {
            ConnectAutoColliderTree(child);
        }
    }

    static void ConnectColliderNode(GameObject current, GameObject dual)
    {
        var dualMeshFilter = dual.GetComponent<MeshFilter>();

        if (dualMeshFilter == null)
        {
            return;
        }

        Debug.Log($"ColliderHelper - ConnectColliderNode - connect {current} to {dual}");

        var dualMesh = dualMeshFilter.sharedMesh;

        var currentCollider = current.GetComponent<MeshCollider>();

        if (currentCollider == null)
        {
            currentCollider = current.AddComponent<MeshCollider>();
        }

        currentCollider.sharedMesh = dualMesh;
    }

    public static GameObject FindColliderDual(GameObject current)
    {
        GameObject result = null;
        var colliderName = GetColliderName(current.name);
        var duals = FindGameObjectsByName(colliderName);

        if (duals.Count == 0)
        {
            return result;
        }

        if (duals.Count == 1)
        {
            return duals[0];
        }

        GameObject bestDual = null;
        var bestScore = 0;

        foreach (var potentialDual in duals)
        {
            var currentAncestors = FindAncestorNames(current);
            var dualAncestors = FindAncestorNames(potentialDual);
            var score = 0;

            for (var i = 0; i < Math.Min(currentAncestors.Count, dualAncestors.Count); i++)
            {
                var n0 = GetColliderName(currentAncestors[i]);
                var n1 = dualAncestors[i];

                if (n0 == n1)
                {
                    score++;
                }
            }

            if (bestScore < score)
            {
                bestScore = score;
                bestDual = potentialDual;
            }
        }

        result = bestDual;

        return result;
    }

    private static List<string> FindAncestorNames(GameObject node)
    {
        var result = new List<string>();

        if (node == null)
        {
            return result;
        }

        while (node != null)
        {
            result.Add(node.name);
            var tr = node.transform;
            var parent = tr?.parent?.gameObject;
            node = parent;
        }

        return result;
    }

    public static List<GameObject> FindGameObjectsByName(string name)
    {
        var result = new List<GameObject>();

        foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>())
        {
            if (obj.name == name)
            {
                result.Add(obj);
            }
        }

        return result;
    }

    public static string GetColliderName(string name)
    {
        if (name.EndsWith("_Col"))
        {
            name = name.Substring(0, name.Length - 4);
        }

        var result = name.Substring(0, Math.Min(63 - 4, name.Length)) + "_Col";
        return result;
    }
}
#endif
