UI/MainMenuHost.razor
@using Sandbox.UI;
@using Sandbox.UI.Navigation;
@using Sandbox;
@namespace Sandbox
@inherits PanelComponent

<root class="mainmenu @(IsInGameMenu ? "in-game" : "front-end") @(IsMenuOpen ? "open" : "closed")">

	@if ( !IsInGameMenu )
	{
		<div class="bg" style="@BgImage(SlotA)" @ref=BgPanelA />
		<div class="bg" style="@BgImage(SlotB)" @ref=BgPanelB />
	}

	<div class="gradient" />

	<NavigationHost DefaultUrl="/" @ref=MenuNavigator>
		<ChildContent>
			<div class="navigator-canvas" slot="navigator-canvas"></div>
			<NavBar Host=@this />
		</ChildContent>
	</NavigationHost>

</root>

@code
{
	const string MainMenuScenePath = "/scenes/menu.scene";

	public NavigationHost MenuNavigator { get; protected set; }
	public static MainMenuHost Current { get; private set; }
	[Property] public bool IsInGameMenu { get; set; }
	public bool IsMainMenuScene => !IsInGameMenu;
	public bool IsMenuOpen { get; private set; } = true;

	List<string> Backgrounds { get; set; } = new();
	int BgIndex { get; set; } = 0;
	int SlotA { get; set; } = 0;
	int SlotB { get; set; } = 1;
	TimeSince BgTimer { get; set; } = 0;

	Panel BgPanelA { get; set; }
	Panel BgPanelB { get; set; }

	const float SizeVel = 0.3f / 30f;
	const float AngleVel = -5f / 30f;

	float SizeA = 1f; float AngleA = 0f; float AlphaA = 255f; float AlphaVelA = 0f;
	float SizeB = 1f; float AngleB = 0f; float AlphaB = 0f;  float AlphaVelB = 0f;

	bool _bgLoaded = false;

	string BgUrl( int idx ) => Backgrounds.Count > 0 ? Backgrounds[idx % Backgrounds.Count] : "/ui/bg.jpg";
	string BgImage( int slot ) => $"background-image: url('{BgUrl( slot )}');";

	protected override void OnEnabled()
	{
		base.OnEnabled();
		Current = this;
		IsMenuOpen = !IsInGameMenu;
		ApplyHostClasses();
	}

	protected override void OnDisabled()
	{
		base.OnDisabled();
		if ( Current == this )
			Current = null!;
	}

	void LoadBackgrounds()
	{
		var files = FileSystem.Mounted.FindFile( "backgrounds", "*", false );
		Backgrounds = files
			.Where( f => f.EndsWith( ".jpg", System.StringComparison.OrdinalIgnoreCase ) ||
			             f.EndsWith( ".png", System.StringComparison.OrdinalIgnoreCase ) )
			.Select( f => "/backgrounds/" + f )
			.OrderBy( _ => Guid.NewGuid() )
			.ToList();
		if ( Backgrounds.Count == 0 )
			Backgrounds = new List<string> { "/ui/bg.jpg" };

		BgIndex = 0; SlotA = 0; SlotB = Backgrounds.Count > 1 ? 1 : 0;
		SizeA = 1f; AngleA = 0f; AlphaA = 255f; AlphaVelA = 0f;
		SizeB = 1f; AngleB = 0f; AlphaB = 0f;   AlphaVelB = 0f;
		StateHasChanged();
	}

	void ApplyPanelStyle( Panel p, float size, float angle, float alpha, bool isOutgoing )
	{
		if ( p is null ) return;
		p.Style.Set( "transform", $"scale({size:F4}) rotate({angle:F4}deg)" );
		p.Style.Set( "opacity",   $"{alpha / 255f:F4}" );
		p.Style.Set( "z-index",   isOutgoing ? "2" : "1" );
	}

	protected override void OnUpdate()
	{
		if ( IsInGameMenu )
		{
			if ( Input.EscapePressed )
			{
				TogglePauseMenu();
				Input.EscapePressed = false;
			}

			return;
		}

		Input.EscapePressed = false;

		if ( !_bgLoaded && BgPanelA is not null )
		{
			_bgLoaded = true;
			LoadBackgrounds();
		}

		if ( Backgrounds.Count == 0 ) return;

		float dt = Time.Delta;

		SizeA  += ( SizeVel / SizeA ) * dt;
		AngleA += AngleVel * dt;
		if ( AlphaVelA > 0f ) AlphaA = MathF.Max( 0f, AlphaA - AlphaVelA * dt );

		SizeB  += ( SizeVel / SizeB ) * dt;
		AngleB += AngleVel * dt;
		if ( AlphaVelB > 0f ) AlphaB = MathF.Max( 0f, AlphaB - AlphaVelB * dt );

		ApplyPanelStyle( BgPanelA, SizeA, AngleA, AlphaA, AlphaVelA > 0f );
		ApplyPanelStyle( BgPanelB, SizeB, AngleB, AlphaB, AlphaVelB > 0f );

		if ( BgTimer > 30f && Backgrounds.Count > 1 )
		{
			BgTimer = 0;
			var nextIdx = ( BgIndex + 1 ) % Backgrounds.Count;

			if ( BgIndex == SlotA )
			{
				AlphaVelA = 255f;
				SlotB = nextIdx;
				BgIndex = SlotB;
				SizeB = 1f; AngleB = 0f; AlphaB = 255f; AlphaVelB = 0f;
			}
			else
			{
				AlphaVelB = 255f;
				SlotA = nextIdx;
				BgIndex = SlotA;
				SizeA = 1f; AngleA = 0f; AlphaA = 255f; AlphaVelA = 0f;
			}
			StateHasChanged();
		}
	}

	public void ResumeGame()
	{
		if ( !IsInGameMenu ) return;

		IsMenuOpen = false;
		ApplyHostClasses();
		StateHasChanged();
	}

	public void TogglePauseMenu()
	{
		if ( !IsInGameMenu ) return;

		IsMenuOpen = !IsMenuOpen;
		if ( IsMenuOpen )
			MenuNavigator?.Navigate( "/" );

		ApplyHostClasses();
		StateHasChanged();
	}

	public void DisconnectToMainMenu()
	{
		Networking.Disconnect();
		ClearLaunchArguments();

		var options = new SceneLoadOptions
		{
			ShowLoadingScreen = true
		};

		if ( !options.SetScene( MainMenuScenePath ) )
			return;

		var sceneFile = options.GetSceneFile();
		Game.ActiveScene.RunEvent<ISceneStartup>( x => x.OnHostPreInitialize( sceneFile ) );

		if ( !Game.ActiveScene.Load( options ) )
			return;

		Game.ActiveScene.RunEvent<ISceneStartup>( x => x.OnHostInitialize() );

		if ( !Application.IsDedicatedServer )
		{
			Game.ActiveScene.RunEvent<ISceneStartup>( x => x.OnClientInitialize() );
		}
	}

	void ClearLaunchArguments()
	{
		LaunchArguments.Map = null;
		LaunchArguments.MaxPlayers = 0;
		LaunchArguments.ServerName = null;
		LaunchArguments.Privacy = Sandbox.Network.LobbyPrivacy.Public;
		LaunchArguments.GameSettings = null;
	}

	void ApplyHostClasses()
	{
		if ( Panel is null ) return;

		Panel.SetClass( "front-end", !IsInGameMenu );
		Panel.SetClass( "in-game", IsInGameMenu );
		Panel.SetClass( "open", IsMenuOpen );
		Panel.SetClass( "closed", !IsMenuOpen );
	}
}