Editor/Util/PanelUtils.cs
using Sandbox;
using Sandbox.Diagnostics;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Panelize;

internal static class PanelUtils
{
	private static Type styleType = typeof( Styles );
	private static PropertyInfo[] styleProperties;
	private static Styles styleReference;
	static PanelUtils()
	{
		MakeReferenceStyle();
	}
	[EditorEvent.Hotload]
	private static void MakeReferenceStyle()
	{
		styleProperties = styleType.GetProperties();
		styleReference = new();
		styleReference.FillDefaults();
	}
	public static PropertyInfo[] GetStyleProperties()
	{
		return styleProperties;
	}
	public static PropertyInfo GetGeneratedProperty(string nameCSS)
	{
		if ( nameCSS == "color" ) // Special case
		{
			return styleType.GetProperty( "FontColor", BindingFlags.Instance);
		}

		string name = nameCSS.ToTitleCase().Replace( " ", "" );
		//Log.Info( $"PropName {nameCSS} -> {name}" );

		return styleType.GetProperty( name );
	}
	public static string GetStyleProperty(PropertyInfo prop)
	{
		string name = "";
		char previous = default;
		foreach(char c in prop.Name)
		{
			if(char.IsUpper(c))
			{
				if(previous != default)
					name += '-';
				name += char.ToLower(c);
			}
			else
			{
				name += c;
			}

			previous = c;
		}

		return name;
	}
	public static string GetStyleDefaultValue(PropertyInfo prop)
	{
		string value = "";

		Type type = prop.PropertyType;
		if(type.Name.Contains("Nullable"))
		{
			type = type.GetGenericArguments()[0];
		}

		if ( type == typeof( Length ) )
			return "auto";
		if(prop.Name == "FontFamily")
			return "Arial";

		if( type.IsValueType)
		{
			value = Activator.CreateInstance( type, true ).ToString();
		}

		if ( !styleType.IsAssignableTo(prop.ReflectedType) )
			return value;

		string propValue = prop.GetValue( styleReference )?.ToString();
		value = LowerLetters(propValue);

		//Log.Info( $"StyleDefaultValue {prop.Name} = '{value}'" );
		return value;
	}
	public static T GetDefaultStyle<T>(Panel p, string name)
	{
		if ( p == null ) return default;

		var prop = styleType.GetProperty( name );
		if(prop == null ) return default;

		if( prop.GetValue( p.Style ) is T value)
			return value;

		return default;
	}
	public static T GetDefaultStyle<T>(Panel p, PropertyInfo prop) => GetDefaultStyle<T>( p, prop.Name );
	public static void DirtyStyles(Panel p)
	{
		p.CallMethod( "DirtyStylesRecursive" );
	}
	public static Widget CreateEditor( StyleBlock block, PropertyInfo prop, IStyleBlock.StyleProperty styleProp, Panel panel, bool rawEdit = false )
	{
		Assert.True( prop.CanRead );

		bool readOnly = !prop.CanWrite;

		Type type = prop.PropertyType;
		if ( type.IsGenericType && type.Name.Contains( "Nullable" ) )
		{
			// Stupid check because we cant compare to Nullable<> for some reason...
			type = type.GetGenericArguments()[0];
		}

		string propName = styleProp.Name;
		CustomProperty sp = new( type, () => GetStyle(block, type, propName), (val) => SetStyle(block, type, propName, val, panel) );
		Widget editor = null;

		if(!rawEdit)
		{
			if ( type == typeof( PositionMode ) )
			{
				editor = new PanelPositionModeControl( sp );
			}
			else if ( type == typeof( Align ) )
			{
				editor = new PanelAlignControl( sp );
			}
			else if ( type == typeof( Justify ) )
			{
				editor = new PanelJustifyControl( sp );
			}
			else if ( type == typeof( FlexDirection ) )
			{
				editor = new FlexDirectionControl( sp );
			}
			else if ( type == typeof( BackgroundRepeat ) )
			{
				editor = new BackgroundRepeatControl( sp );
			}
			else if ( type == typeof( TextAlign ) )
			{
				editor = new TextAlignControl( sp );
			}
			else if ( type == typeof( TextOverflow ) )
			{
				editor = new TextOverflowControl( sp );
			}
			else if ( type == typeof( WordBreak ) )
			{
				editor = new WordBreakControl( sp );
			}
			else if ( type == typeof( TextTransform ) )
			{
				editor = new TextTransformControl( sp );
			}
			else if ( type == typeof( OverflowMode ) )
			{
				editor = new OverflowModeControl( sp );
			}
			else if ( type == typeof( MaskMode ) )
			{
				editor = new MaskModeControl( sp );
			}
			else if ( type == typeof( MaskScope ) )
			{
				editor = new MaskScopeControl( sp );
			}
			else if ( type == typeof( Length ) )
			{
				LengthControl control = new();
				control.Bind( sp );
				editor = control;
			}
		}
		else
		{
			var rawEditor = new StringControlWidget( sp );
			editor = rawEditor;
		}

		if(editor == null)
		{
			editor = ControlWidget.Create( sp );
			if ( editor == null )
			{
				editor = new StringControlWidget( sp );
				Log.Warning( $"Created default editor for type {type} - {editor}" );
			}
		}
		

		return editor;
	}

	/// <summary>
	/// Turns letters into lowercase and ignores non-letters
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	private static string LowerLetters( string input )
	{
		if ( string.IsNullOrEmpty( input ) )
			return "";

		string value = "";
		if ( !string.IsNullOrEmpty( input ) )
		{
			foreach ( char c in input )
			{
				if ( char.IsLetter( c ) )
					value += char.ToLower( c );
				else
					value += c;
			}
		}

		return value;
	}
	private static object GetStyle( StyleBlock block, Type type, string name )
	{
		var value = block.GetRawValues().FirstOrDefault( v => v.Name == name ).Value;
		//Log.Info( $"GetStyle {string.Join( ' ', block.SelectorStrings )} [{name} = {ParseStyle( type, value )}]" );

		return ParseStyle( type, value );
	}

	private static void SetStyle(StyleBlock block, Type type, string name, object value, Panel panel )
	{
		//Log.Info( $"SetStyle {string.Join(' ', block.SelectorStrings)} [{name} = {StyleToString( type, value )} ({value})]" );
		block.SetRawValue( name, StyleToString(type, value) );
		DirtyStyles( panel );
	}

	private static object ParseStyle(Type type, string value)
	{
		object r = default;
		if ( type.IsEnum )
		{
			r = Enum.Parse( type, value, true );
		}
		else if ( type == typeof( Length ) )
		{
			r = Length.Parse( value );
		}
		else if(type == typeof(Color))
		{
			r = Color.Parse( value );
		}
		else
		{
			r = Convert.ChangeType( value, type );
		}

		return r;
	}

	private static string StyleToString(Type type, object value)
	{
		if ( value is Color color )
			return color.ToString( true, true );

		return LowerLetters(value.ToString());
	}
}