UI/Num.cs
namespace Monolith;

/// <summary>
/// Short number formatting for an incremental game. Values here run from 1 to well past a
/// quintillion, so everything user-facing goes through this.
/// </summary>
public static class Num
{
	private static readonly string[] Suffixes =
	{
		"", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc",
	};

	public static string Short( double value )
	{
		if ( double.IsNaN( value ) || double.IsInfinity( value ) )
			return "0";

		bool negative = value < 0;
		value = Math.Abs( value );

		if ( value < 1000 )
			return (negative ? "-" : "") + ((long)value).ToString( "N0" );

		int tier = 0;
		while ( value >= 1000 && tier < Suffixes.Length - 1 )
		{
			value /= 1000;
			tier++;
		}

		var text = value < 10 ? value.ToString( "0.00" )
			: value < 100 ? value.ToString( "0.0" )
			: value.ToString( "0" );

		return (negative ? "-" : "") + text + Suffixes[tier];
	}

	public static string Short( long value ) => Short( (double)value );

	public static string Percent( float fraction, int decimals = 1 )
		=> (fraction * 100f).ToString( "F" + decimals ) + "%";

	/// <summary>Seconds as a speedrun time: 1:23.4, or 1h02:33 once it runs long.</summary>
	public static string Duration( double seconds )
	{
		if ( seconds <= 0 || double.IsNaN( seconds ) )
			return "-";

		int total = (int)seconds;
		int hours = total / 3600;
		int minutes = (total % 3600) / 60;
		int secs = total % 60;

		if ( hours > 0 )
			return $"{hours}h{minutes:00}:{secs:00}";

		double frac = seconds - total;
		return $"{minutes}:{secs:00}.{(int)(frac * 10)}";
	}
}