Game/CausalGameManager.cs
namespace Causal;

public sealed class CausalGameManager : Component
{
	[Property] public GameObject Player { get; set; }
	[Property] public float MenuFadeSeconds { get; set; } = 0.6f;
	[Property] public float CameraFlightSeconds { get; set; } = 2.2f;

	public static CausalGameManager Instance { get; private set; }

	public GameState State { get; private set; } = GameState.Menu;

	public bool IsActive => State == GameState.Active;
	public bool IsInIntro => State == GameState.Intro;

	private PlayerController _controller;
	private TimeSince _timeSinceIntro;
	private Vector3 _flightFromPosition;
	private Rotation _flightFromRotation;

	protected override void OnAwake()
	{
		Instance = this;
	}

	protected override void OnStart()
	{
		ResolvePlayer();
		EnterMenu();
	}

	protected override void OnUpdate()
	{
		if ( State != GameState.Intro )
		{
			return;
		}

		TickIntro();
	}

	protected override void OnDestroy()
	{
		if ( Instance == this )
		{
			Instance = null;
		}
	}

	public void EnterMenu()
	{
		State = GameState.Menu;
		ApplyInputState();
	}

	public void BeginIntro()
	{
		if ( State != GameState.Menu )
		{
			return;
		}

		if ( !_controller.IsValid() )
		{
			Log.Warning( $"CausalGameManager on '{GameObject.Name}' found no PlayerController." );
			return;
		}

		if ( !Scene.Camera.IsValid() )
		{
			EnterGame();
			return;
		}

		_flightFromPosition = Scene.Camera.WorldPosition;
		_flightFromRotation = Scene.Camera.WorldRotation;
		_timeSinceIntro = 0;
		State = GameState.Intro;
		ApplyInputState();
	}

	public void EnterGame()
	{
		if ( State == GameState.Active )
		{
			return;
		}

		State = GameState.Active;
		ApplyInputState();
	}

	private void TickIntro()
	{
		if ( !_controller.IsValid() || !Scene.Camera.IsValid() )
		{
			EnterGame();
			return;
		}

		float flightDuration = CameraFlightSeconds <= 0.01f ? 0.01f : CameraFlightSeconds;
		float flightTime = _timeSinceIntro - MenuFadeSeconds;
		float t = flightTime / flightDuration;
		if ( t < 0f )
		{
			t = 0f;
		}
		if ( t > 1f )
		{
			t = 1f;
		}

		float eased = t * t * (3f - 2f * t);

		Transform target = _controller.EyeTransform;
		Scene.Camera.WorldPosition = Vector3.Lerp( _flightFromPosition, target.Position, eased );
		Scene.Camera.WorldRotation = Rotation.Slerp( _flightFromRotation, target.Rotation, eased );

		if ( t >= 1f )
		{
			EnterGame();
		}
	}

	private void ResolvePlayer()
	{
		if ( Player.IsValid() )
		{
			_controller = Player.GetComponent<PlayerController>();
		}

		if ( !_controller.IsValid() )
		{
			foreach ( var controller in Scene.GetAllComponents<PlayerController>() )
			{
				_controller = controller;
				Player = controller.GameObject;
				break;
			}
		}

		if ( !_controller.IsValid() )
		{
			Log.Warning( $"CausalGameManager on '{GameObject.Name}' found no PlayerController." );
		}
	}

	private void ApplyInputState()
	{
		if ( !_controller.IsValid() )
		{
			return;
		}

		bool active = State == GameState.Active;
		_controller.WishVelocity = 0;
		_controller.UseInputControls = active;
		_controller.UseCameraControls = active;
		_controller.UseLookControls = active;
	}

	public enum GameState
	{
		Menu,
		Intro,
		Active
	}
}