UI/SpawnMenuHost.razor
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@namespace Sandbox

<root @onmousedown=@OnMenuClicked>

    <div class="menu-wrapper">
        <div class="menu-backdrop"></div>
        <SpawnMenuModeBar></SpawnMenuModeBar>
        <div class="menu-slide">
            <PanelSwitcher @ref="PanelSwitcher"></PanelSwitcher>
        </div>
    </div>

</root>

@code
{
    bool stickOpen = false;
    bool lastState;
    string _lastMode = "SpawnMenu";

    PanelSwitcher PanelSwitcher = default;

    protected override void OnUpdate()
    {
        UpdateSpawnMenuState();
        ScanForNewModes();

        var activeType = GetActiveMode()?.GetType().Name;
        SetClass( "no-backdrop", activeType is "ContextMenuHost" or "SaveMenu" or "EffectsHost" );

        base.OnUpdate();
    }

    void UpdateSpawnMenuState()
    {
        var openSpawnMenu = Input.Down("spawnmenu");
        var openInspectMenu = Input.Down("inspectmenu");
        var openAnyMenu = openSpawnMenu || openInspectMenu;

        stickOpen = stickOpen || (Sandbox.UI.InputFocus.Current?.Ancestors.Contains(Panel) ?? false);

        if (stickOpen && Input.Pressed("spawnmenu")) stickOpen = false;

        bool state = stickOpen || openAnyMenu;

        SetClass("open", state);

        if (lastState != state )
        {
            // closed
            if (!state)
            {
                Popup.CloseAll();
            }

            // opened
            if (state)
            {
                if (Input.Pressed("inspectmenu"))
                {
                    Hints.Current.Cancel("openinspectmenu");
                    SwitchMode("ContextMenuHost", false);
                    PanelSwitcher.SkipTransitions();
                }

                if (Input.Pressed("spawnmenu"))
                {
                    Hints.Current.Cancel("openspawnmenu");
                    SwitchMode(_lastMode, false);
                    PanelSwitcher.SkipTransitions();
                    Sandbox.Services.Stats.Increment( "menu.spawnmenu.open", 1 );
                }
            }
        }
        else if ( state )
        {
            // menu stays open — switch modes if the other key is pressed
            if (Input.Pressed("inspectmenu"))
            {
                SwitchMode("ContextMenuHost", false);
            }
            else if (Input.Pressed("spawnmenu"))
            {
                SwitchMode(_lastMode, false);
            }
        }



        lastState = state;
    }

    // When clicking anywhere in the spawn menu, blur any focused text inputs
    // so we can close the menu by pressing the spawnmenu key.
    void OnMenuClicked()
    {
        Sandbox.UI.InputFocus.Clear();
    }

    void ScanForNewModes()
    {
        if ( PanelSwitcher == null )
            return;

        var modes = Game.TypeLibrary.GetTypesWithAttribute<SpawnMenuMode>().OrderBy( x => x.Type.Name != "SpawnMenu" ).ToList();

        // Remove panels whose condition is no longer met
        foreach ( var child in PanelSwitcher.Children.ToList() )
        {
            var mode = modes.FirstOrDefault( m => m.Type.TargetType == child.GetType() );
            if ( mode.Type != null && !mode.Attribute.CheckCondition() )
            {
                if ( PanelSwitcher.ActivePanel == child )
                    SwitchMode( "SpawnMenu" );
                child.Delete();
            }
        }

        // Create panels for modes whose condition is met
        foreach ( var type in modes )
        {
            if ( !type.Attribute.CheckCondition() ) continue;
            if ( PanelSwitcher.Children.Any( x => x.GetType() == type.Type.TargetType ) ) continue;

            var panel = type.Type.Create<Panel>();
            if ( panel == null ) continue;

            PanelSwitcher.AddChild( panel );
        }
    }

    public static void SwitchMode( string modeName, bool remember = true )
    {
        var host = Game.ActiveScene.Get<SpawnMenuHost>();
        if (host?.PanelSwitcher == null) return;

        if (remember) host._lastMode = modeName;

        foreach ( var child in host.PanelSwitcher.Children )
        {
            if ( child.GetType().Name == modeName )
            {
                host.PanelSwitcher.SwitchToPanel(child);
                return;
            }
        }
    }

    public static Panel GetActiveMode()
    {
        var host = Game.ActiveScene.Get<SpawnMenuHost>();
        if (host?.PanelSwitcher == null) return null;
        return host.PanelSwitcher.ActivePanel;
    }

    public interface ISpawnMenuCondition
    {
        static abstract bool IsVisible();
    }

    public class SpawnMenuMode : System.Attribute
    {
        public virtual bool CheckCondition() => true;
    }

    public class SpawnMenuMode<T> : SpawnMenuMode where T : ISpawnMenuCondition
    {
        public override bool CheckCondition() => T.IsVisible();
    }

    public class HostOnly : ISpawnMenuCondition
    {
        public static bool IsVisible() => Networking.IsHost;
    }
}