Code/XGUI/Elements/Toolbar.cs
using Sandbox;
using Sandbox.UI;
using System;
using System.Linq;

namespace XGUI;

/// <summary>
/// A toolbar component that can contain menu items, buttons, separators, and other controls.
/// Supports horizontal or vertical arrangement, and optional drag-to-reorder functionality.
/// </summary>
[Library( "toolbar" )]
public class Toolbar : Panel
{
	public Panel ToolbarItems { get; private set; }
	public Panel DragHandle { get; private set; }

	private bool _isDraggable = false;
	private bool _isVertical = false;
	private bool _isDragging = false;
	private Vector2 _dragStartPosition;
	private Panel _dragGhost;

	/// <summary>
	/// Gets or sets whether this toolbar can be dragged to reposition.
	/// </summary>
	public bool IsDraggable
	{
		get => _isDraggable;
		set
		{
			_isDraggable = value;
			DragHandle.Style.Display = _isDraggable ? DisplayMode.Flex : DisplayMode.None;
			SetClass( "has-drag-handle", _isDraggable );
		}
	}

	/// <summary>
	/// Gets or sets whether this toolbar is arranged vertically.
	/// </summary>
	public bool IsVertical
	{
		get => _isVertical;
		set
		{
			_isVertical = value;
			SetClass( "vertical", _isVertical );
			Style.FlexDirection = _isVertical ? FlexDirection.Column : FlexDirection.Row;
			ToolbarItems.Style.FlexDirection = _isVertical ? FlexDirection.Column : FlexDirection.Row;
		}
	}

	public Toolbar()
	{
		AddClass( "toolbar" );

		// Create the drag handle
		DragHandle = AddChild<Panel>( "drag-handle" );
		DragHandle.AddClass( "toolbar-drag-handle" );
		DragHandle.Style.Display = DisplayMode.None; // Hidden by default

		// Add drag event handlers
		DragHandle.AddEventListener( "onmousedown", OnDragHandleMouseDown );

		// Create container for toolbar items
		ToolbarItems = AddChild<Panel>( "toolbar-items" );
		ToolbarItems.AddClass( "toolbar-items" );

		// Set default orientation
		IsVertical = false;
	}

	public override void Tick()
	{
		base.Tick();

		if ( _isDragging )
		{
			UpdateDragPosition();
		}
	}

	private void OnDragHandleMouseDown( PanelEvent e )
	{
		if ( !_isDraggable || _isDragging )
			return;

		_isDragging = true;
		_dragStartPosition = Mouse.Position;

		// Create a ghost representation of this toolbar for dragging
		CreateDragGhost();

		// Add global mouse event handlers
		AddEventListener( "onmousemove", OnMouseMove );
		AddEventListener( "onmouseup", OnMouseUp );

		e.StopPropagation();
	}

	private void OnMouseMove( PanelEvent e )
	{
		if ( !_isDragging )
			return;

		UpdateDragPosition();
		e.StopPropagation();
	}

	private void OnMouseUp( PanelEvent e )
	{
		if ( !_isDragging )
			return;

		FinishDrag();
		e.StopPropagation();
	}

	private void CreateDragGhost()
	{
		_dragGhost = new Panel();
		_dragGhost.AddClass( "toolbar-drag-ghost" );
		_dragGhost.Style.Position = PositionMode.Absolute;
		_dragGhost.Style.Width = Box.Rect.Width;
		_dragGhost.Style.Height = Box.Rect.Height;
		_dragGhost.Style.BackgroundColor = Color.White.WithAlpha( 0.5f );
		_dragGhost.Style.Opacity = 0.7f;

		// Add the ghost to the root panel to make it float above everything
		var root = FindRootPanel();
		if ( root != null )
		{
			root.AddChild( _dragGhost );
		}
	}

	private void UpdateDragPosition()
	{
		if ( _dragGhost == null )
			return;

		var delta = Mouse.Position - _dragStartPosition;
		_dragGhost.Style.Left = Box.Rect.Left + delta.x;
		_dragGhost.Style.Top = Box.Rect.Top + delta.y;

		// Check for potential toolbar reordering with parent container
		CheckForReordering( delta );
	}

	private void CheckForReordering( Vector2 delta )
	{
		// Find other toolbars in the same container
		var parent = Parent;
		if ( parent == null ) return;

		var siblings = parent.Children.OfType<Toolbar>().Where( t => t != this ).ToList();

		foreach ( var sibling in siblings )
		{
			// Check if we're hovering over this sibling
			var siblingRect = sibling.Box.Rect;
			var ghostPos = new Vector2( _dragGhost.Style.Left.Value.Value, _dragGhost.Style.Top.Value.Value );

			bool isOver = siblingRect.IsInside( ghostPos );

			if ( isOver )
			{
				// Highlight the sibling to show it's a drop target
				sibling.AddClass( "toolbar-drop-target" );
			}
			else
			{
				sibling.RemoveClass( "toolbar-drop-target" );
			}
		}
	}

	private void FinishDrag()
	{
		_isDragging = false;

		// Find the toolbar we're hovering over
		var parent = Parent;
		if ( parent != null )
		{
			var siblings = parent.Children.OfType<Toolbar>().Where( t => t != this ).ToList();
			var targetToolbar = siblings.FirstOrDefault( s => s.HasClass( "toolbar-drop-target" ) );

			if ( targetToolbar != null )
			{
				// Get the index of the two toolbars
				int myIndex = parent.GetChildIndex( this );
				int targetIndex = parent.GetChildIndex( targetToolbar );

				// Move this toolbar to before or after the target
				if ( myIndex != targetIndex )
				{
					parent.SetChildIndex( this, targetIndex );
				}

				// Remove highlight
				targetToolbar.RemoveClass( "toolbar-drop-target" );
			}
		}

		// Clean up
		if ( _dragGhost != null )
		{
			_dragGhost.Delete();
			_dragGhost = null;
		}

		//RemoveEventListener( "onmousemove", OnMouseMove );
		//RemoveEventListener( "onmouseup", OnMouseUp );
	}

	/// <summary>
	/// Adds a menu item to the toolbar that will open a context menu when clicked.
	/// </summary>
	public Button AddMenuItem( string text, Action<ContextMenu> menuPopulator = null )
	{
		var menuItem = new Button();
		menuItem.AddClass( "menu-item" );
		menuItem.Text = text;

		menuItem.AddEventListener( "onclick", () =>
		{
			// Close any existing menus first
			CloseAllMenus();

			// Create and show the context menu
			var contextMenu = new ContextMenu( menuItem, ContextMenu.PositionMode.BelowLeft, 0 );
			menuPopulator?.Invoke( contextMenu );

			// Mark this menu item as active
			menuItem.AddClass( "active" );

			// Listen for menu close event to remove active class
			contextMenu.AddEventListener( "onmousedown", ( e ) => e.StopPropagation() );
		} );

		// Handle mouse enter for menu navigation
		menuItem.AddEventListener( "onmouseenter", () =>
		{
			// If any context menu is open and this isn't already active,
			// trigger click to switch menus
			if ( FindRootPanel().Children.OfType<ContextMenu>().Any() &&
				!menuItem.HasClass( "active" ) )
			{
				menuItem.CreateEvent( "onclick" );
			}
		} );

		ToolbarItems.AddChild( menuItem );
		return menuItem;
	}

	/// <summary>
	/// Adds a standard button to the toolbar.
	/// </summary>
	public ToolbarButton AddButton( string text = null, string icon = null, Action onClick = null )
	{
		var button = new ToolbarButton( text, icon );

		if ( onClick != null )
			button.AddEventListener( "onclick", () => onClick() );

		ToolbarItems.AddChild( button );
		return button;
	}

	public Panel AddDropdownButton(
		string text,
		string icon = null,
		Action onClick = null,
		Action<ContextMenu> dropdownMenuPopulator = null
	)
	{
		// Create a container panel for the split button
		var splitPanel = new Panel();
		splitPanel.AddClass( "toolbar-split-button" );

		// Main button
		var mainButton = new ToolbarButton( text, icon, split: true );
		if ( onClick != null )
			mainButton.AddEventListener( "onclick", () => onClick() );
		mainButton.AddClass( "toolbar-split-main" );
		splitPanel.AddChild( mainButton );

		// Dropdown arrow button
		var dropdownButton = new ToolbarButton( split: true );
		dropdownButton.AddClass( "toolbar-split-dropdown" );
		dropdownButton.AddClass( "toolbar-button" );
		dropdownButton.AddEventListener( "onclick", e =>
		{
			if ( dropdownMenuPopulator != null )
			{
				var menu = new ContextMenu( splitPanel, ContextMenu.PositionMode.BelowLeft, 0 );
				dropdownMenuPopulator( menu );
			}
			e.StopPropagation();
		} );

		// Hover events to color the main icon
		splitPanel.AddEventListener( "onmouseover", e =>
		{
			if ( mainButton.ToolbarIcon != null )
				mainButton.ToolbarIcon.Variant = null; // colored
		} );
		splitPanel.AddEventListener( "onmouseout", e =>
		{

			// if mouse still within the split button, don't reset icon
			if ( splitPanel.HasHovered )
				return;

			if ( mainButton.ToolbarIcon != null )
				mainButton.ToolbarIcon.Variant = "greyscale";
		} );

		// Add a down arrow icon
		var arrowIcon = new Label { Text = "▼" };
		arrowIcon.AddClass( "toolbar-split-arrow-icon" );
		dropdownButton.AddChild( arrowIcon );

		splitPanel.AddChild( dropdownButton );

		ToolbarItems.AddChild( splitPanel );
		return splitPanel;
	}

	/// <summary>
	/// Adds a separator to the toolbar.
	/// </summary>
	public Panel AddSeparator()
	{
		var separator = new Panel();
		separator.AddClass( "toolbar-separator" );
		ToolbarItems.AddChild( separator );
		return separator;
	}

	/// <summary>
	/// Adds a generic panel to the toolbar.
	/// </summary>
	public T AddItem<T>() where T : Panel, new()
	{
		var item = new T();
		item.AddClass( "toolbar-item" );
		ToolbarItems.AddChild( item );
		return item;
	}

	/// <summary>
	/// Adds a generic panel to the toolbar.
	/// </summary>
	public Panel AddItem( Panel item )
	{
		item.AddClass( "toolbar-item" );
		ToolbarItems.AddChild( item );
		return item;
	}

	/// <summary>
	/// Clear all items from the toolbar.
	/// </summary>
	public void Clear()
	{
		ToolbarItems.DeleteChildren();
	}

	/// <summary>
	/// Close all open context menus in the application
	/// </summary>
	public void CloseAllMenus()
	{
		// Find and remove active class from all menu items
		foreach ( var menuItem in ToolbarItems.Children.OfType<Button>()
			.Where( b => b.HasClass( "menu-item" ) && b.HasClass( "active" ) ) )
		{
			menuItem.RemoveClass( "active" );
		}

		// Delete all context menus
		foreach ( var menu in FindRootPanel().Children.OfType<ContextMenu>().ToList() )
		{
			menu.Delete( true );
		}
	}
	public override void SetProperty( string name, string value )
	{
		switch ( name )
		{
			case "draggable":
				{
					IsDraggable = bool.Parse( value );
					return;
				}
			default:
				{
					base.SetProperty( name, value );
					break;
				}
		}
	}
}

[Library( "toolbarcontainer" )]
public class ToolbarContainer : Panel
{
	public ToolbarContainer()
	{
		AddClass( "toolbar-container" );
	}
}


public class ToolbarButton : Button
{
	public XGUIIconPanel ToolbarIcon;
	public ToolbarButton( string text = null, string icon = "", bool split = false )
	{
		AddClass( "toolbar-button" );

		if ( !string.IsNullOrEmpty( icon ) )
		{
			ToolbarIcon = new XGUIIconPanel( icon, iconSize: 20 );
			AddChild( ToolbarIcon );
			ToolbarIcon.Variant = "greyscale";
		}

		if ( !string.IsNullOrEmpty( text ) )
			AddChild( new Label() { Text = text } );

		if ( split )
			return;
		AddEventListener( "onmouseover", OnMouseEnter );
		AddEventListener( "onmouseout", OnMouseLeave );
	}

	private void OnMouseEnter( PanelEvent e )
	{
		if ( ToolbarIcon != null )
			ToolbarIcon.Variant = null;
	}
	private void OnMouseLeave( PanelEvent e )
	{

		if ( ToolbarIcon != null && !HasHovered )
			ToolbarIcon.Variant = "greyscale";
	}
}