GameFlow.cs
namespace PixelPusher;

public enum GameScreen
{
	Title,
	ModePick,
	Pixel,
	Stage,
	World,
	Chip,
	Library,
	Play2D,
	Play3D,
	Pause,
	Cleared
}

public static class GameFlow
{
	public static GameScreen Screen { get; private set; } = GameScreen.Title;
	public static GameScreen ResumeTo { get; private set; } = GameScreen.Pixel;
	public static bool PlaytestFromStage { get; private set; }
	public static GameScreen AfterPlay { get; private set; } = GameScreen.ModePick;
	public static int Ui { get; private set; }
	public static string Toast { get; private set; } = "";
	static float _toastLeft;

	public static bool InStudio =>
		Screen is GameScreen.Pixel or GameScreen.Stage or GameScreen.World or GameScreen.Chip or GameScreen.Library;

	public static bool InPlay => Screen is GameScreen.Play2D or GameScreen.Play3D;

	public static void Bump() => Ui++;

	public static void Tick()
	{
		if ( _toastLeft > 0f )
		{
			_toastLeft -= Time.Delta;
			if ( _toastLeft <= 0f )
			{
				Toast = "";
				Bump();
			}
		}

		if ( Screen == GameScreen.Title )
			return;

		if ( Input.Pressed( "Pause" ) || Input.Pressed( "Menu" ) )
		{
			TogglePause();
			return;
		}

		if ( InStudio )
		{
			if ( Input.Pressed( "Slot1" ) ) OpenStudio( GameScreen.Pixel );
			else if ( Input.Pressed( "Slot2" ) ) OpenStudio( GameScreen.Stage );
			else if ( Input.Pressed( "Slot3" ) ) OpenStudio( GameScreen.World );
			else if ( Input.Pressed( "Slot4" ) ) OpenStudio( GameScreen.Chip );
			else if ( Input.Pressed( "Slot5" ) ) OpenStudio( GameScreen.Library );

			if ( Screen == GameScreen.Stage && Input.Pressed( "Score" ) )
				StartPlaytest();
		}
	}

	public static void ShowToast( string text, float seconds = 2.2f )
	{
		Toast = text ?? "";
		_toastLeft = seconds;
		ChipVoices.Tick();
		Bump();
	}

	public static void GoTitle()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		Screen = GameScreen.Title;
		Bump();
	}

	public static void OpenModePick()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Screen = GameScreen.ModePick;
		Bump();
	}

	public static void EnterBuild()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Lesson.MaybeStart();
		Coach.OnEnterBuild();
		OpenStudio( GameScreen.Pixel );
	}

	public static void StartTour()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Lesson.Start();
		Coach.OnEnterBuild();
		OpenStudio( GameScreen.Pixel );
	}

	public static void EnterPlay()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		PlaySim.Start( Desk.Stage, fromPlaytest: false );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		Bump();
	}

	public static void EnterWalk()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		PlaySim.Start( Desk.Stage, fromPlaytest: false, walk3d: true );
		Coach.OnWalk();
		Screen = GameScreen.Play3D;
		Bump();
	}

	public static void StartWorldWalk()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = true;
		AfterPlay = GameScreen.World;
		PlaySim.Start( Desk.Stage, fromPlaytest: true, walk3d: true );
		Coach.OnWalk();
		Screen = GameScreen.Play3D;
		Bump();
	}

	public static void OpenStudio( GameScreen studio )
	{
		if ( studio is GameScreen.Play2D or GameScreen.Play3D or GameScreen.Pause or GameScreen.Cleared or GameScreen.Title )
			return;

		Desk.Autosave( quiet: true );
		ResumeTo = studio;
		Screen = studio;
		if ( studio == GameScreen.World )
			WorldStudio.EnsureFramed();
		if ( studio is GameScreen.Stage or GameScreen.Library )
		{
			var start = Desk.Stage?.Find( "start" );
			if ( start is not null && Desk.Stage is not null )
				StageStudio.FocusPlayer( start.Tx * Desk.Stage.TileSize, start.Ty * Desk.Stage.TileSize );
		}
		Lesson.OnStudio( studio );
		Coach.OnStudio( studio );
		Bump();
	}

	public static void StartPlaytest()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = true;
		AfterPlay = GameScreen.Stage;
		PlaySim.Start( Desk.Stage, fromPlaytest: true, dropX: StageStudio.DropX, dropY: StageStudio.DropY );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		Bump();
	}

	public static void LeavePlay()
	{
		var to = AfterPlay;
		if ( PlaytestFromStage && to == GameScreen.Stage )
			StageStudio.FocusPlayer( PlaySim.Px, PlaySim.Py );
		if ( PlaytestFromStage && to == GameScreen.World )
			WorldStudio.FocusPlayer( PlaySim.Px, PlaySim.Py, PlaySim.Pz );

		PlaytestFromStage = false;
		Screen = to == GameScreen.Title ? GameScreen.ModePick : to;
		Bump();
	}

	public static void TogglePause()
	{
		if ( Screen == GameScreen.Pause )
		{
			Screen = ResumeTo;
			Bump();
			return;
		}

		if ( Screen == GameScreen.Title || Screen == GameScreen.ModePick )
			return;

		if ( Screen == GameScreen.Cleared )
		{
			LeavePlay();
			return;
		}

		Desk.Autosave( quiet: true );
		ResumeTo = Screen;
		Screen = GameScreen.Pause;
		Bump();
	}

	public static void Resume()
	{
		if ( Screen != GameScreen.Pause )
			return;
		Screen = ResumeTo;
		Bump();
	}

	public static void PlayNextSample()
	{
		var id = StarterPack.NextStageId( Desk.Stage?.Name );
		if ( string.IsNullOrEmpty( id ) || !StarterPack.LoadShelf( id ) )
		{
			LeavePlay();
			return;
		}

		AfterPlay = GameScreen.Stage;
		PlaytestFromStage = true;
		PlaySim.Start( Desk.Stage, fromPlaytest: true );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		ShowToast( Desk.Name );
		Bump();
	}

	public static void Replay()
	{
		var walk = PlaySim.Walk3D;
		var after = AfterPlay;
		var fromStudio = after is GameScreen.Stage or GameScreen.World;
		if ( walk )
		{
			PlaySim.Start( Desk.Stage, fromPlaytest: fromStudio, walk3d: true );
			Screen = GameScreen.Play3D;
			Coach.OnWalk();
		}
		else
		{
			var drop = fromStudio;
			PlaySim.Start( Desk.Stage, fromPlaytest: drop, dropX: drop ? StageStudio.DropX : -1, dropY: drop ? StageStudio.DropY : -1 );
			Screen = GameScreen.Play2D;
			Coach.OnPlay();
		}
		Bump();
	}

	public static void OnStageCleared()
	{
		ResumeTo = AfterPlay is GameScreen.Stage or GameScreen.World
			? AfterPlay
			: (PlaySim.Walk3D ? GameScreen.Play3D : GameScreen.Play2D);
		Screen = GameScreen.Cleared;
		Coach.OnCleared();
		Bump();
	}

	public static void Quit()
	{
		Desk.Autosave( quiet: true );
		Game.Close();
	}
}