TailBox/Domain/Utilities/TailBoxTheme.cs
using System;
using System.Collections.Generic;

namespace Sandbox.TailBox;

internal sealed class TailBoxTheme
{
	public Dictionary<string, string> Spacing { get; init; } = EmptyScale();
	public Dictionary<string, string> Colors { get; init; } = EmptyScale();
	public Dictionary<string, string> FontSizes { get; init; } = EmptyScale();
	public Dictionary<string, string> Radii { get; init; } = EmptyScale();
	public Dictionary<string, string> Shadows { get; init; } = EmptyScale();
	public Dictionary<string, string> Screens { get; init; } = EmptyScale();
	public Dictionary<string, string> BorderWidths { get; init; } = EmptyScale();
	public Dictionary<string, string> Opacity { get; init; } = EmptyScale();
	public Dictionary<string, string> ZIndex { get; init; } = EmptyScale();
	public Dictionary<string, string> Durations { get; init; } = EmptyScale();
	public Dictionary<string, string> Easings { get; init; } = EmptyScale();
	public Dictionary<string, string> FontFamilies { get; init; } = EmptyScale();
	public Dictionary<string, string> LineHeights { get; init; } = EmptyScale();
	public Dictionary<string, string> LetterSpacing { get; init; } = EmptyScale();
	public Dictionary<string, string> TextDecorationThickness { get; init; } = EmptyScale();
	public Dictionary<string, string> TextUnderlineOffset { get; init; } = EmptyScale();
	public Dictionary<string, string> TextShadows { get; init; } = EmptyScale();
	public Dictionary<string, string> Filters { get; init; } = EmptyScale();
	public Dictionary<string, string> BackdropFilters { get; init; } = EmptyScale();
	public Dictionary<string, string> Transforms { get; init; } = EmptyScale();
	public Dictionary<string, string> Animations { get; init; } = EmptyScale();

	public static TailBoxTheme CreateEmpty()
	{
		return new TailBoxTheme();
	}

	public bool TrySpacing( string key, out string value )
	{
		return TryLength( key, Spacing, out value );
	}

	public bool TryLength( string key, Dictionary<string, string> scale, out string value )
	{
		value = null;
		if ( string.IsNullOrWhiteSpace( key ) )
			return false;

		if ( TryArbitrary( key, out value ) )
			return true;

		if ( key == "full" )
			value = "100%";
		else if ( key == "screen" )
			value = "100vw";
		else if ( key == "auto" )
			value = "auto";
		else if ( TryFraction( key, out var fraction ) )
			value = fraction;
		else if ( scale.TryGetValue( key, out var scaled ) )
			value = scaled;

		return value is not null;
	}

	public bool TryColor( string key, out string color )
	{
		if ( TryArbitrary( key, out color ) )
			return true;

		return Colors.TryGetValue( key, out color );
	}

	public bool TryArbitrary( string key, out string value )
	{
		value = null;
		if ( string.IsNullOrWhiteSpace( key ) || key.Length < 3 || key[0] != '[' || key[^1] != ']' )
			return false;

		value = TailBoxText.DecodeArbitraryValue( key[1..^1] );
		return !string.IsNullOrWhiteSpace( value );
	}

	public bool TryFraction( string key, out string value )
	{
		value = null;
		var parts = key.Split( '/', StringSplitOptions.RemoveEmptyEntries );
		if ( parts.Length != 2 )
			return false;

		if ( !double.TryParse( parts[0], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var numerator )
			|| !double.TryParse( parts[1], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var denominator )
			|| Math.Abs( denominator ) < double.Epsilon )
		{
			return false;
		}

		value = (numerator / denominator * 100.0).ToString( "0.######", System.Globalization.CultureInfo.InvariantCulture ) + "%";
		return true;
	}

	public static string ApplyNegative( string value, bool negative )
	{
		if ( !negative || string.IsNullOrWhiteSpace( value ) || value == "0" || value.StartsWith( "-", StringComparison.Ordinal ) || value == "auto" )
			return value;

		return "-" + value;
	}

	private static Dictionary<string, string> EmptyScale()
	{
		return new Dictionary<string, string>( StringComparer.OrdinalIgnoreCase );
	}
}