PixelText.cs
namespace Sandbox;

/// <summary>
/// Utility for drawing bitmap text using the "text" sprite font data.
/// Each glyph is 3×5 pixels; characters are spaced 4px apart (3 + 1 gap).
/// Supports lowercase a-z and digits 0-9. Unknown characters are skipped.
///
/// Usage:
///   PixelText.Draw( 10, 10, "hello", new Color32( 255, 255, 255, 255 ) );
/// </summary>
public static class PixelText
{
	/// <summary>Glyph width in pixels.</summary>
	public const int GlyphWidth = 3;

	/// <summary>Glyph height in pixels.</summary>
	public const int GlyphHeight = 5;

	/// <summary>Horizontal advance per character (glyph + 1px gap).</summary>
	public const int Advance = 4;

	/// <summary>Vertical line spacing (glyph + 1px gap).</summary>
	public const int LineHeight = 6;

	/// <summary>
	/// Draw a string into the <see cref="PixelScreen"/> at the given position.
	/// Text is rendered left-to-right; newlines (\n) move to the next row.
	/// The <paramref name="color"/> replaces the font's default black pixels.
	/// </summary>
	public static void Draw( int x, int y, string text, Color32 color )
	{
		PixelScreen screen = PixelScreen.Instance;
		if ( screen is null )
			return;

		// Ensure text sprites are loaded.
		if ( !SpriteManager.IsLoaded )
			SpriteManager.LoadAll();

		int cursorX = x;
		int cursorY = y;
		Color32 black = new Color32( 0, 0, 0, 255 );

		foreach ( char c in text )
		{
			if ( c == '\n' )
			{
				cursorX = x;
				cursorY -= LineHeight; // Y goes up in our coordinate system.
				continue;
			}

			if ( c == ' ' )
			{
				cursorX += Advance;
				continue;
			}

			// The text sprite uses the character itself as the animation name.
			string glyphName = char.ToLowerInvariant( c ).ToString();
			AnimationData anim = SpriteManager.GetAnimation( "text", glyphName );

			if ( anim is null || anim.Frames.Count == 0 )
			{
				cursorX += Advance;
				continue;
			}

			FrameData frame = anim.Frames[0];

			foreach ( PixelData pixel in frame.Pixels )
			{
				int px = cursorX + pixel.Position.X;
				int py = cursorY + pixel.Position.Y;

				// Replace the font's black with the requested colour.
				Color32 drawColor = (pixel.Color.r == black.r && pixel.Color.g == black.g && pixel.Color.b == black.b)
					? color
					: pixel.Color;

				if ( drawColor.a == 255 )
					screen.SetPixel( px, py, drawColor );
				else if ( drawColor.a > 0 )
					screen.AddPixel( px, py, drawColor );
			}

			cursorX += Advance;
		}
	}

	/// <summary>
	/// Measure the pixel width a string would occupy when drawn.
	/// </summary>
	public static int MeasureWidth( string text )
	{
		int maxWidth = 0;
		int lineWidth = 0;

		foreach ( char c in text )
		{
			if ( c == '\n' )
			{
				maxWidth = Math.Max( maxWidth, lineWidth );
				lineWidth = 0;
				continue;
			}

			lineWidth += Advance;
		}

		maxWidth = Math.Max( maxWidth, lineWidth );

		// Remove the trailing 1px gap from the last character.
		return maxWidth > 0 ? maxWidth - 1 : 0;
	}

	/// <summary>
	/// Draw text centered horizontally within the screen.
	/// </summary>
	public static void DrawCentered( int y, string text, Color32 color )
	{
		PixelScreen screen = PixelScreen.Instance;
		if ( screen is null )
			return;

		int width = MeasureWidth( text );
		int x = (screen.PixelWidth - width) / 2;
		Draw( x, y, text, color );
	}
}