Editor/Inspector/XGUIPanelInspector.cs
using Editor;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace XGUI.XGUIEditor
{
	/// <summary>
	/// Inspects properties of a MarkupNode and provides UI for editing
	/// </summary>
	public class PanelInspector : Widget
	{
		public XGUIDesigner OwnerDesigner;
		private Panel _targetPanel;
		private MarkupNode _targetNode;

		// Sections
		private Widget _generalSection;
		private Widget _styleSection;

		// Property groups
		private Dictionary<string, PropertyGroup> _generalGroups = new Dictionary<string, PropertyGroup>();
		private Dictionary<string, PropertyGroup> _styleGroups = new Dictionary<string, PropertyGroup>();

		// Tracks all created editors for efficient updates
		private Dictionary<string, PropertyEditor> _generalEditors = new Dictionary<string, PropertyEditor>();
		private Dictionary<string, PropertyEditor> _styleEditors = new Dictionary<string, PropertyEditor>( StringComparer.OrdinalIgnoreCase );

		// Scroll area container
		private ScrollArea _scrollArea;
		private Widget _contentContainer;

		/// <summary>
		/// Action triggered when a property potentially changes a source attribute or content.
		/// </summary>
		public Action<MarkupNode, string, object> OnPropertyChanged;

		public PanelInspector( Widget parent = null ) : base( parent )
		{
			// Set up the layout for PanelInspector to fill available space
			Layout = Layout.Column();

			// Create a ScrollArea to contain all inspector contents
			_scrollArea = Layout.Add( new ScrollArea( null ), 1 );
			_scrollArea.SetSizeMode( SizeMode.Expand, SizeMode.Expand );
			_scrollArea.MaximumSize = new Vector2( float.MaxValue, float.MaxValue );

			// Create a content container inside the scroll area
			_contentContainer = new Widget( null );
			_contentContainer.Layout = Layout.Flow();
			_contentContainer.Layout.Spacing = 5;
			_contentContainer.Layout.Margin = 10;

			// Add the content container to the scroll area
			_scrollArea.Canvas = _contentContainer;

			CreateSections();
		}

		private void CreateSections()
		{
			_generalSection = AddSection( "General", "description", true );
			_styleSection = AddSection( "Style", "brush", true );
		}

		private Widget AddSection( string title, string icon, bool initiallyExpanded )
		{
			var expandGroup = new ExpandGroup( _contentContainer );
			expandGroup.StateCookieName = $"PanelInspector.{title}";
			expandGroup.Icon = icon;
			expandGroup.Title = title;
			expandGroup.SetOpenState( initiallyExpanded );
			var container = new Widget( null ) { Layout = Layout.Column() };
			container.Layout.Spacing = 4;
			container.Layout.Margin = new Margin( 8 );
			container.MinimumSize = new Vector2( 0, 10 ); // Ensure it doesn't collapse completely
			expandGroup.SetWidget( container );
			_contentContainer.Layout.Add( expandGroup );
			return container;
		}

		public void SetTarget( Panel panel, MarkupNode node, bool rebuild = true )
		{
			bool sameTarget = (_targetPanel == panel && _targetNode == node);

			_targetPanel = panel;
			_targetNode = node;

			if ( rebuild )
			{
				Rebuild( true );
			}
			else
			{
				Rebuild( false );
			}
		}

		public void Rebuild( bool forceFullRebuild = true )
		{
			if ( _targetNode == null || _targetNode.Type != NodeType.Element )
			{
				ClearAllSections();
				ClearEditorReferences();
				return; // Nothing to inspect or not an element
			}

			// Only perform a full rebuild when necessary
			if ( forceFullRebuild )
			{
				ClearAllSections();
				ClearEditorReferences();
				RebuildGeneralSection();
				RebuildStyleSection();
			}
			else
			{
				// Update individual properties without rebuilding the entire UI
				UpdatePropertiesIncremental();
			}
		}

		private void ClearEditorReferences()
		{
			_generalEditors.Clear();
			_styleEditors.Clear();
		}

		private void ClearAllSections()
		{
			_generalSection?.Layout.Clear( true );
			_styleSection?.Layout.Clear( true );
		}

		private void RebuildGeneralSection()
		{
			var layout = _generalSection.Layout;
			layout.Clear( true );
			_generalGroups.Clear();
			_generalEditors.Clear();

			if ( _targetNode == null || _targetNode.Type != NodeType.Element ) return;

			// Create core properties group
			var coreGroup = CreatePropertyGroup( "Core", layout, _generalGroups );

			// Add type property (read-only)
			var typeEditor = coreGroup.AddEditor<TextPropertyEditor>( "type", "Type" );
			typeEditor.SetValueSilently( _targetNode.TagName );
			typeEditor.RootWidget.Enabled = false; // Make read-only

			// Add class property
			string currentClasses = _targetNode.Attributes.GetValueOrDefault( "class", "" );
			var classEditor = coreGroup.AddEditor<TextPropertyEditor>( "class", "Classes" );
			classEditor.SetValueSilently( currentClasses );
			classEditor.ValueChanged += ( value ) =>
			{
				string strValue = value.ToString().Trim();
				_targetNode.Attributes["class"] = strValue;
				if ( _targetPanel != null ) UpdatePanelClasses( _targetPanel, strValue );
				OnPropertyChanged?.Invoke( _targetNode, "class", strValue );
			};
			_generalEditors["class"] = classEditor;

			// Add ID property
			string currentId = _targetNode.Attributes.GetValueOrDefault( "id", "" );
			var idEditor = coreGroup.AddEditor<TextPropertyEditor>( "id", "ID" );
			idEditor.SetValueSilently( currentId );
			idEditor.ValueChanged += ( value ) =>
			{
				string strValue = value.ToString().Trim();
				_targetNode.Attributes["id"] = strValue;
				OnPropertyChanged?.Invoke( _targetNode, "id", strValue );
			};
			_generalEditors["id"] = idEditor;

			// Add element-specific properties
			AddElementSpecificProperties( _targetNode, _targetPanel );
		}

		private void AddElementSpecificProperties( MarkupNode node, Panel panel )
		{
			string tag = node.TagName.ToLowerInvariant();
			string innerText = node.Children.FirstOrDefault( c => c.Type == NodeType.Text && !string.IsNullOrWhiteSpace( c.TextContent ) )?.TextContent ?? "";

			var contentGroup = CreatePropertyGroup( "Content", _generalSection.Layout, _generalGroups );

			// Handle window root node (tag="root")
			if ( tag == "root" && panel is XGUI.Window window )
			{
				// Theme group
				var themeGroup = CreatePropertyGroup( "Theme", _generalSection.Layout, _generalGroups );

				// Theme dropdown
				var themeEditor = themeGroup.AddDropdownEditor( "theme", "Window Theme",
					FileSystem.Mounted.FindFile( "/XGUI/DefaultStyles/", "*.scss" )
					.Select( f => System.IO.Path.GetFileNameWithoutExtension( f ) )
					.ToArray() );

				// Get current theme path filename without extension for the dropdown
				string currentThemeFilename = System.IO.Path.GetFileNameWithoutExtension( OwnerDesigner.CurrentTheme );
				themeEditor.SetValueSilently( currentThemeFilename );

				themeEditor.ValueChanged += ( value ) =>
				{
					string themeName = value.ToString();
					string fullThemePath = $"/XGUI/DefaultStyles/{themeName}.scss";

					// Update the designer's current theme
					OwnerDesigner.CurrentTheme = fullThemePath;

					// Apply the theme to the window if it exists
					if ( window != null )
					{
						window.SetTheme( fullThemePath );
					}

					OnPropertyChanged?.Invoke( node, "theme", themeName );
				};
				_generalEditors["theme"] = themeEditor;

				// Window title property
				string titleAttr = node.Attributes.GetValueOrDefault( "title", "Window" );
				var titleEditor = contentGroup.AddEditor<TextPropertyEditor>( "title", "Window Title" );
				titleEditor.SetValueSilently( titleAttr );
				titleEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["title"] = strValue;
					if ( window != null ) window.Title = strValue;
					OnPropertyChanged?.Invoke( node, "title", strValue );
				};
				_generalEditors["title"] = titleEditor;

				// Window Dimensions Group
				var dimensionsGroup = CreatePropertyGroup( "Dimensions", _generalSection.Layout, _generalGroups );

				// Width property
				string widthAttr = node.Attributes.GetValueOrDefault( "width", "" );
				var widthEditor = dimensionsGroup.AddEditor<TextPropertyEditor>( "width", "Width" );
				widthEditor.SetValueSilently( widthAttr );
				widthEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["width"] = strValue;
					if ( window != null && int.TryParse( strValue, out var length ) )
						window.Style.Width = length;
					OnPropertyChanged?.Invoke( node, "width", strValue );
				};
				_generalEditors["width"] = widthEditor;

				// Height property
				string heightAttr = node.Attributes.GetValueOrDefault( "height", "" );
				var heightEditor = dimensionsGroup.AddEditor<TextPropertyEditor>( "height", "Height" );
				heightEditor.SetValueSilently( heightAttr );
				heightEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["height"] = strValue;
					if ( window != null && int.TryParse( strValue, out var length ) )
						window.Style.Height = length;
					OnPropertyChanged?.Invoke( node, "height", strValue );
				};
				_generalEditors["height"] = heightEditor;

				// Min width property
				string minWidthAttr = node.Attributes.GetValueOrDefault( "minwidth", "" );
				var minWidthEditor = dimensionsGroup.AddEditor<TextPropertyEditor>( "minwidth", "Min Width" );
				minWidthEditor.SetValueSilently( minWidthAttr );
				minWidthEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["minwidth"] = strValue;
					if ( window != null && int.TryParse( strValue, out var length ) )
						window.MinSize = new Vector2( length, window.MinSize.y );
					OnPropertyChanged?.Invoke( node, "minwidth", strValue );
				};
				_generalEditors["minwidth"] = minWidthEditor;

				// Min height property
				string minHeightAttr = node.Attributes.GetValueOrDefault( "minheight", "" );
				var minHeightEditor = dimensionsGroup.AddEditor<TextPropertyEditor>( "minheight", "Min Height" );
				minHeightEditor.SetValueSilently( minHeightAttr );
				minHeightEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["minheight"] = strValue;
					if ( window != null && int.TryParse( strValue, out var length ) )
						window.MinSize = new Vector2( window.MinSize.x, length );
					OnPropertyChanged?.Invoke( node, "minheight", strValue );
				};
				_generalEditors["minheight"] = minHeightEditor;

				// Position group
				var positionGroup = CreatePropertyGroup( "Position", _generalSection.Layout, _generalGroups );

				// X position
				string xPosAttr = node.Attributes.GetValueOrDefault( "x", "" );
				var xPosEditor = positionGroup.AddEditor<TextPropertyEditor>( "x", "X Position" );
				xPosEditor.SetValueSilently( xPosAttr );
				xPosEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["x"] = strValue;
					if ( window != null && float.TryParse( strValue, out var floatVal ) )
						window.Position = new Vector2( floatVal, window.Position.y );
					OnPropertyChanged?.Invoke( node, "x", strValue );
				};
				_generalEditors["x"] = xPosEditor;

				// Y position
				string yPosAttr = node.Attributes.GetValueOrDefault( "y", "" );
				var yPosEditor = positionGroup.AddEditor<TextPropertyEditor>( "y", "Y Position" );
				yPosEditor.SetValueSilently( yPosAttr );
				yPosEditor.ValueChanged += ( value ) =>
				{
					string strValue = value.ToString();
					node.Attributes["y"] = strValue;
					if ( window != null && float.TryParse( strValue, out var floatVal ) )
						window.Position = new Vector2( window.Position.x, floatVal );
					OnPropertyChanged?.Invoke( node, "y", strValue );
				};
				_generalEditors["y"] = yPosEditor;

				// Controls group
				var controlsGroup = CreatePropertyGroup( "Window Controls", _generalSection.Layout, _generalGroups );

				// Has Close button
				bool hasClose = node.Attributes.GetValueOrDefault( "hasclose", "true" ).Equals( "true", StringComparison.OrdinalIgnoreCase );
				var hasCloseEditor = controlsGroup.AddEditor<BoolPropertyEditor>( "hasclose", "Close Button" );
				hasCloseEditor.SetValueSilently( hasClose ? "true" : "" );
				hasCloseEditor.ValueChanged += ( value ) =>
				{
					bool boolValue = (bool)value;
					node.Attributes["hasclose"] = boolValue ? "true" : "false";
					if ( window != null ) window.HasClose = boolValue;
					OnPropertyChanged?.Invoke( node, "hasclose", boolValue );
				};
				_generalEditors["hasclose"] = hasCloseEditor;

				// Has Minimize button
				bool hasMinimize = node.Attributes.GetValueOrDefault( "hasminimise", "false" ).Equals( "true", StringComparison.OrdinalIgnoreCase );
				var hasMinimizeEditor = controlsGroup.AddEditor<BoolPropertyEditor>( "hasminimise", "Minimize Button" );
				hasMinimizeEditor.SetValueSilently( hasMinimize ? "true" : "" );
				hasMinimizeEditor.ValueChanged += ( value ) =>
				{
					bool boolValue = (bool)value;
					node.Attributes["hasminimise"] = boolValue ? "true" : "false";
					if ( window != null ) window.HasMinimise = boolValue;
					OnPropertyChanged?.Invoke( node, "hasminimise", boolValue );
				};
				_generalEditors["hasminimise"] = hasMinimizeEditor;

				// Has Maximize button
				bool hasMaximize = node.Attributes.GetValueOrDefault( "hasmaximise", "false" ).Equals( "true", StringComparison.OrdinalIgnoreCase );
				var hasMaximizeEditor = controlsGroup.AddEditor<BoolPropertyEditor>( "hasmaximise", "Maximize Button" );
				hasMaximizeEditor.SetValueSilently( hasMaximize ? "true" : "" );
				hasMaximizeEditor.ValueChanged += ( value ) =>
				{
					bool boolValue = (bool)value;
					node.Attributes["hasmaximise"] = boolValue ? "true" : "false";
					if ( window != null ) window.HasMaximise = boolValue;
					OnPropertyChanged?.Invoke( node, "hasmaximise", boolValue );
				};
				_generalEditors["hasmaximise"] = hasMaximizeEditor;

				// Behavior group
				var behaviorGroup = CreatePropertyGroup( "Behavior", _generalSection.Layout, _generalGroups );

				// Is Draggable
				bool isDraggable = node.Attributes.GetValueOrDefault( "isdraggable", "true" ).Equals( "true", StringComparison.OrdinalIgnoreCase );
				var isDraggableEditor = behaviorGroup.AddEditor<BoolPropertyEditor>( "isdraggable", "Draggable" );
				isDraggableEditor.SetValueSilently( isDraggable ? "true" : "" );
				isDraggableEditor.ValueChanged += ( value ) =>
				{
					bool boolValue = (bool)value;
					node.Attributes["isdraggable"] = boolValue ? "true" : "false";
					if ( window != null ) window.IsDraggable = boolValue;
					OnPropertyChanged?.Invoke( node, "isdraggable", boolValue );
				};
				_generalEditors["isdraggable"] = isDraggableEditor;

				// Is Resizable
				bool isResizable = node.Attributes.GetValueOrDefault( "isresizable", "true" ).Equals( "true", StringComparison.OrdinalIgnoreCase );
				var isResizableEditor = behaviorGroup.AddEditor<BoolPropertyEditor>( "isresizable", "Resizable" );
				isResizableEditor.SetValueSilently( isResizable ? "true" : "" );
				isResizableEditor.ValueChanged += ( value ) =>
				{
					bool boolValue = (bool)value;
					node.Attributes["isresizable"] = boolValue ? "true" : "false";
					if ( window != null ) window.IsResizable = boolValue;
					OnPropertyChanged?.Invoke( node, "isresizable", boolValue );
				};
				_generalEditors["isresizable"] = isResizableEditor;

				return; // We've handled the window properties, so exit the method
			}

			switch ( tag )
			{
				case "button":
				case "label":
					var textEditor = contentGroup.AddEditor<TextPropertyEditor>( "innertext", "Text" );
					textEditor.SetValueSilently( innerText );
					textEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						UpdateOrCreateChildTextNode( node, strValue );
						if ( panel is Sandbox.UI.Button b ) b.Text = strValue;
						else if ( panel is Sandbox.UI.Label l ) l.Text = strValue;
						OnPropertyChanged?.Invoke( node, "innertext", strValue );
					};
					_generalEditors["innertext"] = textEditor;
					break;

				case "check":
					var labelEditor = contentGroup.AddEditor<TextPropertyEditor>( "innertext", "Label" );
					labelEditor.SetValueSilently( innerText );
					labelEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						UpdateOrCreateChildTextNode( node, strValue );
						if ( panel is XGUI.CheckBox cb ) cb.LabelText = strValue;
						OnPropertyChanged?.Invoke( node, "innertext", strValue );
					};
					_generalEditors["innertext"] = labelEditor;

					bool isChecked = node.Attributes.ContainsKey( "checked" );
					var checkedEditor = contentGroup.AddEditor<BoolPropertyEditor>( "checked", "Checked" );
					checkedEditor.SetValueSilently( isChecked ? "true" : "" );
					checkedEditor.ValueChanged += ( value ) =>
					{
						bool boolValue = (bool)value;
						if ( boolValue ) node.Attributes["checked"] = null; // Valueless
						else node.Attributes.Remove( "checked" );
						if ( panel is XGUI.CheckBox cb ) cb.Checked = boolValue;
						OnPropertyChanged?.Invoke( node, "checked", boolValue );
					};
					_generalEditors["checked"] = checkedEditor;
					break;

				case "groupbox":
					string titleAttr = node.Attributes.GetValueOrDefault( "title", "" );
					var titleEditor = contentGroup.AddEditor<TextPropertyEditor>( "title", "Title" );
					titleEditor.SetValueSilently( titleAttr );
					titleEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						node.Attributes["title"] = strValue;
						if ( panel is XGUI.GroupBox gb ) gb.Title = strValue;
						OnPropertyChanged?.Invoke( node, "title", strValue );
					};
					_generalEditors["title"] = titleEditor;
					break;

				case "sliderscale":
					string minAttr = node.Attributes.GetValueOrDefault( "min", "0" );
					var minEditor = contentGroup.AddEditor<TextPropertyEditor>( "min", "Min" );
					minEditor.SetValueSilently( minAttr );
					minEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						node.Attributes["min"] = strValue;
						if ( panel is XGUI.SliderScale sl && float.TryParse( strValue, CultureInfo.InvariantCulture, out var v ) ) sl.MinValue = v;
						OnPropertyChanged?.Invoke( node, "min", strValue );
					};
					_generalEditors["min"] = minEditor;

					string maxAttr = node.Attributes.GetValueOrDefault( "max", "100" );
					var maxEditor = contentGroup.AddEditor<TextPropertyEditor>( "max", "Max" );
					maxEditor.SetValueSilently( maxAttr );
					maxEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						node.Attributes["max"] = strValue;
						if ( panel is XGUI.SliderScale sl && float.TryParse( strValue, CultureInfo.InvariantCulture, out var v ) ) sl.MaxValue = v;
						OnPropertyChanged?.Invoke( node, "max", strValue );
					};
					_generalEditors["max"] = maxEditor;
					break;
				case "tab":
					string tabTitleAttr = node.Attributes.GetValueOrDefault( "tabtext", "" );
					var tabTitleEditor = contentGroup.AddEditor<TextPropertyEditor>( "tabtext", "Title" );
					tabTitleEditor.SetValueSilently( tabTitleAttr );
					tabTitleEditor.ValueChanged += ( value ) =>
					{
						string strValue = value.ToString();
						node.Attributes["tabtext"] = strValue;
						// find owner TabContainer and update its tab text
						if ( panel.Parent.Parent is Sandbox.UI.TabContainer tc )
						{
							Log.Info( $"Found TabContainer: {tc}" );
							var tab = tc.Tabs.FirstOrDefault( t => t.Page == panel );
							if ( tab != null )
							{
								tab.TabName = strValue;
								tab.Button.Text = strValue;
							}
						}

						OnPropertyChanged?.Invoke( node, "tabtext", strValue );
					};
					_generalEditors["tabtext"] = tabTitleEditor;
					break;
			}
		}

		private AlignmentSelectorWidget _alignmentSelector;
		private void RebuildStyleSection()
		{
			var layout = _styleSection.Layout;
			layout.Clear( true );
			_styleGroups.Clear();
			_styleEditors.Clear();

			if ( _targetNode == null || _targetNode.Type != NodeType.Element ) return;

			string styleAttributeValue = _targetNode.Attributes.GetValueOrDefault( "style", "" );
			var currentStyles = ParseStyleAttribute( styleAttributeValue );

			// Add style property groups
			var sizeGroup = CreatePropertyGroup( "Size", layout, _styleGroups );
			var positionGroup = CreatePropertyGroup( "Position", layout, _styleGroups );
			var marginGroup = CreatePropertyGroup( "Margin", layout, _styleGroups );
			var paddingGroup = CreatePropertyGroup( "Padding", layout, _styleGroups );
			var colorGroup = CreatePropertyGroup( "Colors", layout, _styleGroups );

			// Helper to wire up style property editors
			Action<PropertyEditor> connectStyleEditor = ( editor ) =>
			{
				editor.ValueChanged += ( value ) =>
				{
					string cssValue;

					// Handle different editor types
					if ( editor is FloatPropertyEditor floatEditor )
					{
						cssValue = floatEditor.GetFormattedValue();
					}
					else if ( editor is ColorPropertyEditor colorEditor )
					{
						cssValue = colorEditor.GetHexString();
					}
					else
					{
						cssValue = value.ToString();
					}

					//Log.Info( cssValue );

					_targetNode.TryModifyStyle( editor.PropertyName, cssValue );
					if ( _targetPanel != null )
					{
						UpdatePanelSingleStyle( _targetPanel, editor.PropertyName, cssValue );
					}
					OnPropertyChanged?.Invoke( _targetNode, "style", _targetNode.Attributes["style"] );
				};

				_styleEditors[editor.PropertyName] = editor;
			};

			// Create size editors
			var widthEditor = sizeGroup.AddFloatEditor( "width", "Width", true );
			connectStyleEditor( widthEditor );

			var heightEditor = sizeGroup.AddFloatEditor( "height", "Height", true );
			connectStyleEditor( heightEditor );

			// Create position editors
			var leftEditor = positionGroup.AddFloatEditor( "left", "Left", true );
			connectStyleEditor( leftEditor );

			var topEditor = positionGroup.AddFloatEditor( "top", "Top", true );
			connectStyleEditor( topEditor );

			var rightEditor = positionGroup.AddFloatEditor( "right", "Right", true );
			connectStyleEditor( rightEditor );

			var bottomEditor = positionGroup.AddFloatEditor( "bottom", "Bottom", true );
			connectStyleEditor( bottomEditor );

			var positionEditor = positionGroup.AddDropdownEditor(
	"position", "Position", new[] { "relative", "absolute" }, true );
			connectStyleEditor( positionEditor );

			// Create and add our alignment selector
			// Create and add the alignment selector after the position dropdown
			var alignmentSelector = positionGroup.AddEditor<AlignmentSelectorWidget>( "alignment", "Alignment", true );
			_alignmentSelector = (AlignmentSelectorWidget)alignmentSelector; // Store reference

			// Hook up events
			alignmentSelector.ValueChanged += ( value ) =>
			{
				if ( value is AlignmentSelectorWidget.AlignmentChangeInfo change && _targetPanel != null )
				{
					if ( change.Value != null )
					{
						// Apply the new style value
						UpdatePanelSingleStyle( _targetPanel, change.Edge, change.Value );
					}
					else
					{
						// Remove/reset the style property
						switch ( change.Edge )
						{
							case "left": _targetPanel.Style.Left = null; break;
							case "top": _targetPanel.Style.Top = null; break;
							case "right": _targetPanel.Style.Right = null; break;
							case "bottom": _targetPanel.Style.Bottom = null; break;
						}

						// Mark style as dirty to refresh the visual
						_targetPanel.Style.Dirty();
					}

					// Update the corresponding editor if it exists
					if ( _styleEditors.TryGetValue( change.Edge, out var editor ) )
					{
						string newEditorValue = change.Value ?? "";
						editor.SetValueSilently( newEditorValue );
					}
				}
			};

			// Initialize with current styles
			_alignmentSelector.SetTarget( _targetPanel, _targetNode, currentStyles );

			// Connect position editor to update alignment selector
			positionEditor.ValueChanged += ( value ) =>
			{
				string newPositionMode = value.ToString();
				Dictionary<string, string> updatedStyles = ParseStyleAttribute( _targetNode.Attributes.GetValueOrDefault( "style", "" ) );
				_alignmentSelector.SetTarget( _targetPanel, _targetNode, updatedStyles );
			};

			// Create margin editors
			var marginTopEditor = marginGroup.AddFloatEditor( "margin-top", "Top", true );
			connectStyleEditor( marginTopEditor );

			var marginRightEditor = marginGroup.AddFloatEditor( "margin-right", "Right", true );
			connectStyleEditor( marginRightEditor );

			var marginBottomEditor = marginGroup.AddFloatEditor( "margin-bottom", "Bottom", true );
			connectStyleEditor( marginBottomEditor );

			var marginLeftEditor = marginGroup.AddFloatEditor( "margin-left", "Left", true );
			connectStyleEditor( marginLeftEditor );

			// Create padding editors
			var paddingTopEditor = paddingGroup.AddFloatEditor( "padding-top", "Top", true );
			connectStyleEditor( paddingTopEditor );

			var paddingRightEditor = paddingGroup.AddFloatEditor( "padding-right", "Right", true );
			connectStyleEditor( paddingRightEditor );

			var paddingBottomEditor = paddingGroup.AddFloatEditor( "padding-bottom", "Bottom", true );
			connectStyleEditor( paddingBottomEditor );

			var paddingLeftEditor = paddingGroup.AddFloatEditor( "padding-left", "Left", true );
			connectStyleEditor( paddingLeftEditor );

			// Create color editors
			var bgColorEditor = colorGroup.AddColorEditor( "background-color", "Background", true );
			connectStyleEditor( bgColorEditor );

			var textColorEditor = colorGroup.AddColorEditor( "color", "Text", true );
			connectStyleEditor( textColorEditor );

			// Create font size editor
			var fontSizeEditor = colorGroup.AddFloatEditor( "font-size", "Font Size", true );
			connectStyleEditor( fontSizeEditor );

			// Set initial values for all style editors
			UpdateStyleEditors( currentStyles );

			// Add pseudo-class state editors if we have a live panel
			if ( _targetPanel != null )
			{
				var stateGroup = CreatePropertyGroup( "Live State", layout, _styleGroups );

				var hoverEditor = stateGroup.AddEditor<BoolPropertyEditor>( "hover", "Hover", false );
				hoverEditor.SetValueSilently( _targetPanel.HasClass( "hover" ) ? "true" : "" );
				hoverEditor.ValueChanged += ( value ) =>
				{
					bool isChecked = (bool)value;
					if ( isChecked ) _targetPanel.AddClass( "hover" );
					else _targetPanel.RemoveClass( "hover" );
				};

				var activeEditor = stateGroup.AddEditor<BoolPropertyEditor>( "active", "Active", false );
				activeEditor.SetValueSilently( _targetPanel.HasClass( "active" ) ? "true" : "" );
				activeEditor.ValueChanged += ( value ) =>
				{
					bool isChecked = (bool)value;
					if ( isChecked ) _targetPanel.AddClass( "active" );
					else _targetPanel.RemoveClass( "active" );
				};

				var focusEditor = stateGroup.AddEditor<BoolPropertyEditor>( "focus", "Focus", false );
				focusEditor.SetValueSilently( _targetPanel.HasClass( "focus" ) ? "true" : "" );
				focusEditor.ValueChanged += ( value ) =>
				{
					bool isChecked = (bool)value;
					if ( isChecked ) _targetPanel.AddClass( "focus" );
					else _targetPanel.RemoveClass( "focus" );
				};

				stateGroup.GroupLayout.Add( new Editor.Label( "Click to toggle states" ) );
			}
		}

		private PropertyGroup CreatePropertyGroup( string name, Layout parentLayout, Dictionary<string, PropertyGroup> groupDictionary )
		{
			var group = new PropertyGroup( name );
			group.CreateUI( parentLayout );
			groupDictionary[name] = group;
			return group;
		}

		private void UpdatePropertiesIncremental()
		{
			if ( _targetNode == null ) return;

			// Update general properties
			var generalValues = new Dictionary<string, string>();
			generalValues["class"] = _targetNode.Attributes.GetValueOrDefault( "class", "" );
			generalValues["id"] = _targetNode.Attributes.GetValueOrDefault( "id", "" );
			generalValues["innertext"] = _targetNode.Children.FirstOrDefault( c => c.Type == NodeType.Text )?.TextContent ?? "";

			// Add other attributes
			foreach ( var attr in _targetNode.Attributes )
			{
				if ( attr.Key != "style" && attr.Key != "class" && attr.Key != "id" )
				{
					generalValues[attr.Key] = attr.Value;
				}
			}

			foreach ( var editor in _generalEditors )
			{
				if ( generalValues.TryGetValue( editor.Key, out string value ) )
				{
					editor.Value.SetValueSilently( value );
				}
			}

			// Update style properties
			string styleAttributeValue = _targetNode.Attributes.GetValueOrDefault( "style", "" );
			var currentStyles = ParseStyleAttribute( styleAttributeValue );

			UpdateStyleEditors( currentStyles );

			if ( _alignmentSelector != null )
			{
				_alignmentSelector.SetTarget( _targetPanel, _targetNode, currentStyles );
			}
		}

		private void UpdateStyleEditors( Dictionary<string, string> styles )
		{
			foreach ( var editor in _styleEditors )
			{
				string cssProperty = editor.Key;
				string value = styles.GetValueOrDefault( cssProperty, "" );
				editor.Value.SetValueSilently( value );
			}
		}

		// Helper to update a single style property on the live panel
		private void UpdatePanelSingleStyle( Panel panel, string propertyName, string stringValue )
		{
			if ( panel == null ) return;
			try
			{
				switch ( propertyName.ToLowerInvariant() )
				{
					case "width": panel.Style.Width = ParseLength( stringValue ); break;
					case "height": panel.Style.Height = ParseLength( stringValue ); break;
					case "top": panel.Style.Top = ParseLength( stringValue ); break;
					case "left": panel.Style.Left = ParseLength( stringValue ); break;
					case "bottom": panel.Style.Bottom = ParseLength( stringValue ); break;
					case "right": panel.Style.Right = ParseLength( stringValue ); break;
					case "position": panel.Style.Position = stringValue == "absolute" ? PositionMode.Absolute : PositionMode.Relative; break;
					case "margin-top": panel.Style.MarginTop = ParseLength( stringValue ); break;
					case "margin-right": panel.Style.MarginRight = ParseLength( stringValue ); break;
					case "margin-bottom": panel.Style.MarginBottom = ParseLength( stringValue ); break;
					case "margin-left": panel.Style.MarginLeft = ParseLength( stringValue ); break;
					case "padding-top": panel.Style.PaddingTop = ParseLength( stringValue ); break;
					case "padding-right": panel.Style.PaddingRight = ParseLength( stringValue ); break;
					case "padding-bottom": panel.Style.PaddingBottom = ParseLength( stringValue ); break;
					case "padding-left": panel.Style.PaddingLeft = ParseLength( stringValue ); break;
					case "background-color": panel.Style.BackgroundColor = ParseColor( stringValue ); break;
					case "color": panel.Style.FontColor = ParseColor( stringValue ); break;
					case "font-size": panel.Style.FontSize = ParseLength( stringValue ); break;
				}

				panel.Style.Dirty();
			}
			catch ( Exception ex )
			{
				Log.Warning( $"Failed to apply live style preview for '{propertyName}:{stringValue}'. Error: {ex.Message}" );
			}
		}

		// Helper to update live panel classes
		private void UpdatePanelClasses( Panel panel, string classString )
		{
			if ( panel == null ) return;
			foreach ( var cls in panel.Class.ToList() ) panel.RemoveClass( cls );
			foreach ( var cls in classString.Split( ' ', StringSplitOptions.RemoveEmptyEntries ) ) panel.AddClass( cls );
		}

		// Helper to update or create a direct child text node within a MarkupNode
		private void UpdateOrCreateChildTextNode( MarkupNode parentNode, string text )
		{
			if ( parentNode == null ) return;

			var textNode = parentNode.Children.FirstOrDefault( c => c.Type == NodeType.Text );
			if ( textNode != null )
			{
				textNode.TextContent = text;
			}
			else
			{
				parentNode.Children.Insert( 0, new MarkupNode
				{
					Type = NodeType.Text,
					TextContent = text,
					Parent = parentNode
				} );
			}
		}

		/// <summary>
		/// Parses a CSS style attribute string (e.g., "width: 100px; color: red;")
		/// into a dictionary of property-value pairs.
		/// </summary>
		private Dictionary<string, string> ParseStyleAttribute( string styleString )
		{
			var styles = new Dictionary<string, string>( System.StringComparer.OrdinalIgnoreCase );
			if ( string.IsNullOrWhiteSpace( styleString ) )
			{
				return styles;
			}

			var declarations = styleString.Split( ';', System.StringSplitOptions.RemoveEmptyEntries );

			foreach ( var declaration in declarations )
			{
				var parts = declaration.Split( ':', 2 );
				if ( parts.Length == 2 )
				{
					string property = parts[0].Trim();
					string value = parts[1].Trim();

					if ( !string.IsNullOrEmpty( property ) )
					{
						styles[property] = value;
					}
				}
			}
			return styles;
		}

		/// <summary>
		/// Generates a CSS style attribute string from a dictionary of property-value pairs.
		/// </summary>
		private string GenerateStyleAttributeValue( Dictionary<string, string> styles )
		{
			if ( styles == null || styles.Count == 0 )
			{
				return string.Empty;
			}

			var sb = new StringBuilder();

			foreach ( var kvp in styles )
			{
				if ( !string.IsNullOrWhiteSpace( kvp.Value ) )
				{
					sb.Append( kvp.Key.ToLowerInvariant() );
					sb.Append( ": " );
					sb.Append( kvp.Value.Trim() );
					sb.Append( "; " );
				}
			}

			if ( sb.Length > 0 )
			{
				sb.Length -= 1; // Remove trailing space
			}

			return sb.ToString();
		}

		// Parses CSS length (px, %) string
		private Length? ParseLength( string value )
		{
			if ( string.IsNullOrEmpty( value ) )
				return null;

			return Length.Parse( value );
		}

		// Parses CSS color string (#rgb, #rrggbb, rgb(), rgba())
		private Color ParseColor( string colorValue )
		{
			if ( string.IsNullOrEmpty( colorValue ) )
				return Color.White;

			if ( Color.TryParse( colorValue, out var color ) )
				return color;

			return Color.White;
		}

		/// <summary>
		/// Converts a color to CSS hex format
		/// </summary>
		private string ColorToHex( Color color )
		{
			if ( color.a < 1.0f )
			{
				return $"#{(int)(color.r * 255):X2}{(int)(color.g * 255):X2}{(int)(color.b * 255):X2}{(int)(color.a * 255):X2}";
			}
			else
			{
				return $"#{(int)(color.r * 255):X2}{(int)(color.g * 255):X2}{(int)(color.b * 255):X2}";
			}
		}

	}

	// Represents the alignment options for a panel
	public class PanelAlignment
	{
		public bool Left { get; set; } = true;
		public bool Top { get; set; } = true;
		public bool Right { get; set; } = false;
		public bool Bottom { get; set; } = false;

		public PanelAlignment() { }

		public PanelAlignment( bool left, bool top, bool right, bool bottom )
		{
			Left = left;
			Top = top;
			Right = right;
			Bottom = bottom;
		}

		// Parse alignments from style properties
		public static PanelAlignment FromStyles( Dictionary<string, string> styles )
		{
			var alignment = new PanelAlignment();

			// Default is Left+Top if none specified
			bool hasLeft = styles.ContainsKey( "left" );
			bool hasTop = styles.ContainsKey( "top" );
			bool hasRight = styles.ContainsKey( "right" );
			bool hasBottom = styles.ContainsKey( "bottom" );

			// If no positioning is specified, default to left+top
			if ( !hasLeft && !hasTop && !hasRight && !hasBottom )
				return alignment;

			alignment.Left = hasLeft;
			alignment.Top = hasTop;
			alignment.Right = hasRight;
			alignment.Bottom = hasBottom;

			return alignment;
		}
	}
}