Editor/CodeDialog/StateSymbolRow.cs
using Editor;
using Grains.RazorDesigner.Wiring;
using Sandbox;

namespace Grains.RazorDesigner.CodeDialog;

public sealed class StateSymbolRow : Widget
{
    private const string LogPrefix = "[Grains.RazorDesigner]";

    private readonly LineEdit _nameEdit;
    private readonly LineEdit _typeEdit;
    private readonly EnumControlWidget _visibilityWidget;
    private readonly LineEdit _initialEdit;

    private readonly VisibilityProxy _visibilityProxy;
    private readonly SerializedObject _visibilitySerialized;

    public StateSymbol Symbol { get; private set; }
    public event System.Action Changed;
    public event System.Action DeleteRequested;

    public StateSymbolRow( Widget parent, StateSymbol initial ) : base( parent )
    {
        Symbol = initial;
        Layout = Layout.Row();
        Layout.Spacing = 4;

        _nameEdit = new LineEdit( this )
        {
            Text = initial.Name ?? "",
            PlaceholderText = "Count",
        };
        _nameEdit.TextEdited += _ => OnNameEdited();
        Layout.Add( _nameEdit, 2 );

        _typeEdit = new LineEdit( this )
        {
            Text = string.IsNullOrEmpty( initial.Type ) ? "int" : initial.Type,
            PlaceholderText = "string | int | float | bool",
        };
        _typeEdit.TextEdited += _ => OnTypeEdited();
        Layout.Add( _typeEdit, 1 );

        _visibilityProxy = new VisibilityProxy { Visibility = initial.Visibility };
        _visibilitySerialized = EditorTypeLibrary.GetSerializedObject( _visibilityProxy );
        var visProp = _visibilitySerialized.GetProperty( nameof( VisibilityProxy.Visibility ) );
        _visibilityWidget = new EnumControlWidget( visProp );
        _visibilitySerialized.OnPropertyChanged += _ => OnVisibilityChanged();
        Layout.Add( _visibilityWidget, 1 );

        _initialEdit = new LineEdit( this )
        {
            Text = ExpressionPreview.For( initial.Initial ),
            PlaceholderText = "0",
        };
        _initialEdit.TextEdited += _ => OnInitialEdited();
        Layout.Add( _initialEdit, 2 );

        var deleteButton = new Editor.Button( this )
        {
            Icon = "delete",
            ToolTip = "Delete this state symbol",
            FixedWidth = CodeDialog.DeleteButtonWidth,
        };
        deleteButton.MouseLeftPress += () => DeleteRequested?.Invoke();
        Layout.Add( deleteButton );

        Log.Info( $"{LogPrefix} StateSymbolRow ctor: name='{initial.Name}', type='{initial.Type}', visibility={initial.Visibility}" );
    }

    private void OnNameEdited()
    {
        Symbol = Symbol with { Name = ( _nameEdit.Text ?? "" ).Trim() };
        Changed?.Invoke();
    }

    private void OnTypeEdited()
    {
        var newType = ( _typeEdit.Text ?? "" ).Trim();
        if ( string.IsNullOrEmpty( newType ) ) newType = "int";
        Symbol = Symbol with
        {
            Type = newType,
            Initial = InitialValueParser.Parse( newType, _initialEdit.Text ),
        };
        Changed?.Invoke();
    }

    private void OnVisibilityChanged()
    {
        Symbol = Symbol with { Visibility = _visibilityProxy.Visibility };
        Changed?.Invoke();
    }

    private void OnInitialEdited()
    {
        Symbol = Symbol with { Initial = InitialValueParser.Parse( Symbol.Type, _initialEdit.Text ) };
        Changed?.Invoke();
    }

    private sealed class VisibilityProxy
    {
        public SymbolVisibility Visibility { get; set; }
    }
}

internal static class ExpressionPreview
{
    public static string For( Expression expr )
    {
        return expr switch
        {
            null => "",
            LiteralExpression lit when lit.Value.ValueKind == System.Text.Json.JsonValueKind.String
                => lit.Value.GetString() ?? "",
            LiteralExpression lit => lit.Value.GetRawText(),
            InlineExpression inl  => inl.Code,
            _ => "<expression>",
        };
    }
}