using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Theriapolis.Game.Screens;
///
/// Manages a stack of IScreen instances.
/// Only the top screen receives Update and Draw calls.
/// Push/Pop are deferred to the start of the next frame to avoid mid-loop mutation.
///
public sealed class ScreenManager
{
private readonly Game1 _game;
private readonly Stack _stack = new();
private IScreen? _pendingPush;
private int _pendingPops; // count rather than bool — multiple Pops queued in one frame all apply
public ScreenManager(Game1 game)
{
_game = game;
}
public IScreen? Current => _stack.Count > 0 ? _stack.Peek() : null;
public void Push(IScreen screen)
{
_pendingPush = screen;
}
public void Pop()
{
_pendingPops++;
}
/// Process any pending push/pop, then update the current screen.
public void Update(GameTime gameTime)
{
// Apply deferred transitions — drain all pending pops before any push.
bool popped = false;
while (_pendingPops > 0 && _stack.Count > 0)
{
var top = _stack.Pop();
top.Deactivate();
_pendingPops--;
popped = true;
}
_pendingPops = 0;
// Only call Reactivate once after a pop chain — not every frame.
if (popped && _stack.TryPeek(out var back)) back.Reactivate();
if (_pendingPush is not null)
{
_stack.TryPeek(out var cur);
cur?.Deactivate();
_pendingPush.Initialize(_game);
_stack.Push(_pendingPush);
_pendingPush = null;
}
Current?.Update(gameTime);
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
Current?.Draw(gameTime, spriteBatch);
}
}