Editor/TypeResolver.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ExtendedEditor;

public static class TypeResolver
{
    private static readonly Dictionary<string, HashSet<Type>> _sandboxCategories = new()
    {
        {"System", [
            typeof(Color),
            typeof(Color32),
            typeof(Vector2),
            typeof(Vector2Int),
            typeof(Vector3),
            typeof(Vector3Int),
            typeof(Vector4),
            typeof(Angles),
            typeof(Rotation),
            typeof(Rect),
            typeof(RectInt),
            typeof(Sphere),
            typeof(Capsule),
            typeof(GameTransform),
            typeof(TagSet),
        ]},
        {"Network", [
            typeof(OwnerTransfer),
#pragma warning disable CS0612 // Type or member is obsolete
            typeof(NetPermission),
#pragma warning restore CS0612 // Type or member is obsolete
        ]},
        {"Time", [
            typeof(TimeSince),
            typeof(TimeUntil),
            typeof(RealTimeSince),
            typeof(RealTimeUntil),
        ]},
    };

    private static readonly Dictionary<string, List<string>> _sandboxNamedCategories = new()
    {
        { "Physics" , ["Physics", "Collision", "Collider", "Hitbox", "Rigidbody"]},
        { "Scene" , ["Scene"]},
        { "Particle" , ["Particle"]},
        { "Object" , ["Object"]},
        { "Network" , ["Network", "Connection", "Rpc", "StaticRpc"]},
        { "Input" , ["Input", "Mouse", "Keyboard", "Gamepad"]},
        { "Aniamation" , ["Bone"]},
        { "Models" , ["Model", "Vertex", "Polygon", "Mesh"]},
        { "GameObject" , ["GameObject"]},
        { "Scenes" , ["PrefabScene", "Scene"]},
        { "Prefab" , ["Prefab"]},
        { "Audio" , ["Audio", "Sound", "Stereo"]},
        { "Video" , ["Video", "Image", "Render"]},
        { "Debug" , ["Debug"]},
    };

    private static readonly Dictionary<string, HashSet<Type>> _systemCategories = new()
    {
        {"Tuples", [
            typeof(Tuple<>),
            typeof(Tuple<,>),
            typeof(Tuple<,,>),
            typeof(Tuple<,,,>),
            typeof(Tuple<,,,,>),
            typeof(Tuple<,,,,,>),
            typeof(Tuple<,,,,,,>),
            typeof(Tuple<,,,,,,,>),
        ]},
        {"Actions", [
            typeof(Action),
            typeof(Action<>),
            typeof(Action<,>),
            typeof(Action<,,>),
            typeof(Action<,,,>),
            typeof(Action<,,,,>),
            typeof(Action<,,,,,>),
            typeof(Action<,,,,,,>),
            typeof(Action<,,,,,,,>),
            typeof(Action<,,,,,,,,>),
            typeof(Action<,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,,,>),
        ]},
        {"Functions", [
            typeof(Func<>),
            typeof(Func<,>),
            typeof(Func<,,>),
            typeof(Func<,,,>),
            typeof(Func<,,,,>),
            typeof(Func<,,,,,>),
            typeof(Func<,,,,,,>),
            typeof(Func<,,,,,,,>),
            typeof(Func<,,,,,,,,>),
            typeof(Func<,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,,,>),
            typeof(Predicate<>),
        ]},
        {"Date", [
            typeof(DateTime),
            typeof(DateTimeKind),
            typeof(DateTimeOffset),
            typeof(DateOnly),
            typeof(TimeOnly),
            typeof(DayOfWeek),
        ]},
    };

    private static readonly HashSet<Type> _extraSandboxTypes = [
        typeof(TemporaryEffect)
    ];

    public static IReadOnlySet<Type> SystemTypes { get; } = typeof(string).Assembly.GetTypes().Where(t => t.Namespace?.StartsWith("System") ?? false).ToHashSet();



    private static string FormatAssemblyName(Assembly asm)
    {
        var name = asm.GetName().Name!;

        if(name.StartsWith("package.", StringComparison.OrdinalIgnoreCase))
            name = name.Substring("package.".Length);

        if(name.StartsWith("local.", StringComparison.OrdinalIgnoreCase))
            name = name.Substring("local.".Length);

        return name.ToTitleCase();
    }

    private static string GetAssemblyQualifiedPath(Type type)
    {
        var path = string.IsNullOrEmpty(type.Namespace)
            ? FormatAssemblyName(type.Assembly)
            : type.Namespace.Replace('.', '/');

        return path;
    }

    private static string GetTypePath(Type type)
    {
        if(type.DeclaringType is not null)
            return $"{GetTypePath(type.DeclaringType)}/{type.Name}";

        var typeDesc = TypeLibrary.GetType(type);

        var prefix = typeDesc?.Group ?? GetAssemblyQualifiedPath(type);
        if(prefix == "Sandbox System")
            prefix = "Sandbox/System";

        var icon = typeDesc?.Icon;

        if(type.Namespace?.StartsWith("System") ?? false)
        {
            bool categoryFound = false;

            if(type.IsValueType)
                prefix += "/Value Types";

            if(type.IsAssignableTo(typeof(ITuple)))
            {
                prefix += "/Tuples";
            }
            else if(type.IsAssignableTo(typeof(Exception)))
            {
                prefix += "/Exceptions";
            }
            else if(type.IsAssignableTo(typeof(Attribute)))
            {
                prefix += "/Attributes";
            }
            else
            {
                foreach(var (name, category) in _systemCategories)
                {
                    if(category.Contains(type))
                    {
                        prefix += '/' + name;
                        categoryFound = true;
                        break;
                    }
                }
            }

            if(!categoryFound)
            {
                if(type.IsInterface)
                {
                    prefix += "/Interfaces";
                }
            }
        }
        else if((type.Namespace?.StartsWith("Sandbox") ?? false) || _extraSandboxTypes.Contains(type))
        {
            bool categoryFound = true;
            if(type.IsAssignableTo(typeof(Resource)))
            {
                prefix = $"Resource/{prefix}";
                icon ??= "description";
            }
            else if(type.IsAssignableTo(typeof(Component)))
            {
                prefix = $"Component/{prefix}";
                icon ??= "category";
            }
            else
            {
                categoryFound = false;
            }


            if(!categoryFound)
            {
                categoryFound = true;

                if(type.Name.EndsWith("Msg") || type.Name.EndsWith("MsgAck"))
                    prefix += "/Connection/Messages";
                else
                    categoryFound = false;
            }

            if(!categoryFound)
            {
                foreach(var (name, category) in _sandboxCategories)
                {
                    if(category.Contains(type))
                    {
                        prefix += '/' + name;
                        categoryFound = true;
                        break;
                    }
                }
            }

            if(!categoryFound)
            {
                foreach(var (namedCategory, prefixes) in _sandboxNamedCategories)
                {
                    if(prefixes.Any(type.Name.StartsWith))
                    {
                        prefix += '/' + namedCategory;
                        categoryFound = true;
                        break;
                    }
                }
            }

            if(!prefix.StartsWith("Sandbox"))
                prefix = "Sandbox/" + prefix;
        }

        icon ??= "check_box_outline_blank";

        return $"{prefix}/{type.Name}:{icon}@2000";
    }

    public readonly record struct TypeOption
    {
        public Type Type { get; init; }
        public Menu.PathElement[] Path { get; init; }
        public string Title { get; init; }
        public string? Description { get; init; }
        public string? Icon { get; init; }


        public TypeOption(Type type)
        {
            var typeDesc = TypeLibrary.GetType(type);

            Path = Menu.GetSplitPath(GetTypePath(type));
            Type = type;
            Title = type.Name;
            Description = typeDesc?.Description;
            Icon = typeDesc?.Icon;
        }
    }
}