Editor/AssetBrowserAddon/LocationRegistration.Hammer.cs
using System;
using System.Linq;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using Editor;
using Sandbox;

namespace Sandbox.AssetBrowserAddon;

internal static partial class LocationRegistration
{
    private static bool _hammerTypeChecked;
    private static Type _hammerType;
    private static Func<LocalAssetBrowser> _hammerInstanceGetter;

    /// <summary>
    /// Explicitly watch for the Hammer asset browser and attach our header once it exists.
    /// </summary>
    [EditorEvent.Frame]
    private static void TryInstallHammer()
    {
        if (_hammerAttached)
            return;

        if (_hammerType is null && !_hammerTypeChecked)
        {
            _hammerType = AppDomain.CurrentDomain
                .GetAssemblies()
                .SelectManySafe(a => a.GetTypesSafe())
                .FirstOrDefault(t =>
                    t.FullName is not null &&
                    t.FullName.Contains("HammerAssetBrowser", StringComparison.Ordinal) &&
                    typeof(LocalAssetBrowser).IsAssignableFrom(t));

            _hammerTypeChecked = true;

            if (_hammerType is not null)
            {
                _hammerInstanceGetter = CreateHammerGetter(_hammerType);
            }
        }

        if (_hammerType is null || _hammerInstanceGetter is null)
            return;

        var inst = _hammerInstanceGetter();

        if (inst is not LocalAssetBrowser hammer)
            return;

        if (RegisterBrowser(hammer))
        {
            _hammerAttached = true;
            RebuildAll();
        }
    }

    private static Func<LocalAssetBrowser> CreateHammerGetter(Type hammerType)
    {
        var prop = hammerType.GetProperty("Instance", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
        if (prop != null)
        {
            return () => prop.GetValue(null) as LocalAssetBrowser;
        }

        var field = hammerType.GetField("Instance", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
        if (field != null)
        {
            return () => field.GetValue(null) as LocalAssetBrowser;
        }

        return null;
    }

    private static IEnumerable<Type> GetTypesSafe(this Assembly assembly)
    {
        try
        {
            return assembly.GetTypes();
        }
        catch
        {
            return Array.Empty<Type>();
        }
    }

    /// <summary>
    /// Safe helper to flatten types across assemblies.
    /// </summary>
    private static IEnumerable<Type> SelectManySafe(this Assembly[] assemblies, Func<Assembly, IEnumerable<Type>> selector)
    {
        foreach (var asm in assemblies)
        {
            foreach (var type in selector(asm))
            {
                yield return type;
            }
        }
    }
    
}