Code/XGUI/Elements/ListView.cs
using Sandbox;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XGUI;

/// <summary>
/// A Win32-inspired ListView control for XGUI, usable from C# and Razor.
/// </summary>
public class ListView : Panel
{
	public enum ListViewMode
	{
		List,
		Details,
		Icons
	}

	public class ListViewColumn
	{
		public string Header { get; set; }
		public string Field { get; set; }
		public int Width { get; set; } = 100;
	}

	public class ListViewItem : Panel
	{
		private ListView ParentListView { get; }
		public object Data { get; }
		public List<string> SubItems { get; }
		public bool IsSelected { get; private set; }
		private List<Panel> Cells { get; } = new();
		public XGUIIconPanel IconPanel;
		private TextEntry _renameEntry;

		public ListViewItem( ListView parent, object data, List<string> subItems )
		{
			ParentListView = parent;
			Data = data;
			SubItems = subItems;

			AddClass( "listview-item" );

			// Initial render based on parent's current view mode
			UpdateViewMode( ParentListView.ViewMode );
		}

		public bool Draggable = true;

		private bool _isDragging = false;
		private bool _leftMouseDown = false;
		private Vector2 _dragStartScreen;
		private Vector2 _dragStartLocal;
		// Invokable events
		public Action<ItemDragEvent> OnDragStartEvent;
		public Action<ItemDragEvent> OnDragEvent;
		public Action<ItemDragEvent> OnDragEndEvent;

		protected override void OnMouseDown( MousePanelEvent e )
		{
			base.OnMouseDown( e );
			if ( e.MouseButton == MouseButtons.Left )
			{
				SelectSelf();
				_leftMouseDown = true;
			}
			if ( e.MouseButton == MouseButtons.Left && Draggable )
			{
				_dragStartScreen = Mouse.Position;
				_dragStartLocal = MousePosition;
				_isDragging = false; // Will become true on move
			}
			//Log.Info( $"ListViewItem.OnMouseDown: {e.MouseButton} at {e.LocalPosition} (screen: {Mouse.Position})" );
		}

		protected override void OnMouseMove( MousePanelEvent e )
		{
			base.OnMouseMove( e );
			if ( _leftMouseDown && Draggable )
			{
				if ( !_isDragging )
				{
					// Start drag if mouse moved enough (e.g., 3px threshold)
					if ( (Mouse.Position - _dragStartScreen).Length > 3 )
					{
						_isDragging = true;
						//Log.Info( $"ListViewItem.OnMouseMove: Starting drag at {e.LocalPosition} (screen: {Mouse.Position})" );
						OnDragStartEvent?.Invoke( new ItemDragEvent
						{
							LocalGrabPosition = _dragStartLocal,
							ScreenGrabPosition = _dragStartScreen,
							LocalPosition = MousePosition,
							ScreenPosition = Mouse.Position,
							MouseDelta = Mouse.Position - _dragStartScreen
						} );
					}

				}
				else
				{
					// Continue drag
					OnDragEvent?.Invoke( new ItemDragEvent
					{
						LocalGrabPosition = _dragStartLocal,
						ScreenGrabPosition = _dragStartScreen,
						LocalPosition = MousePosition,
						ScreenPosition = Mouse.Position,
						MouseDelta = Mouse.Position - _dragStartScreen
					} );
				}
			}
		}

		protected override void OnMouseUp( MousePanelEvent e )
		{
			base.OnMouseUp( e );
			if ( e.MouseButton == MouseButtons.Left )
			{
				_leftMouseDown = false;
			}
			if ( _isDragging && Draggable )
			{
				_isDragging = false;
				OnDragEndEvent?.Invoke( new ItemDragEvent
				{
					LocalGrabPosition = _dragStartLocal,
					ScreenGrabPosition = _dragStartScreen,
					LocalPosition = MousePosition,
					ScreenPosition = Mouse.Position,
					MouseDelta = Mouse.Position - _dragStartScreen
				} );
			}
		}


		protected override void OnClick( MousePanelEvent e )
		{
			base.OnClick( e );
			SelectSelf();
		}

		protected override void OnDoubleClick( MousePanelEvent e )
		{
			base.OnDoubleClick( e );
			OnItemDoubleClicked();
		}

		public void UpdateViewMode( ListViewMode viewMode )
		{
			// Clear existing cells
			DeleteChildren();

			Cells.Clear();

			// Update classes for the view mode
			SetClass( "listview-row", viewMode == ListViewMode.Details || viewMode == ListViewMode.List );
			SetClass( "listview-icon-item", viewMode == ListViewMode.Icons );

			// Reuse existing icon panel or create a new one
			if ( IconPanel == null )
			{
				IconPanel = new XGUIIconPanel();
			}
			else
			{

				var originalIconName = IconPanel.IconName;
				var originalIconType = IconPanel.IconType;
				var originalIconSize = IconPanel.IconSize;
				IconPanel.Delete();
				IconPanel = new XGUIIconPanel
				{
					IconName = originalIconName,
					IconType = originalIconType,
					IconSize = originalIconSize
				};
			}
			IconPanel.SetClass( "listview-icon", true );

			if ( viewMode == ListViewMode.Details )
			{
				// Create a cell for each column
				for ( int i = 0; i < ParentListView.Columns.Count; i++ )
				{
					var cell = new Panel();
					if ( i == 0 )
					{
						cell.AddChild( IconPanel );
					}
					cell.AddClass( "listview-cell" );
					cell.Style.Width = ParentListView.Columns[i].Width;

					string text = (SubItems != null && i < SubItems.Count) ? SubItems[i] : "";
					var itemtext = cell.AddChild<Panel>( "listview-text" );
					itemtext.AddChild( new Label { Text = text } );

					Cells.Add( cell );
					AddChild( cell );
				}
			}
			else if ( viewMode == ListViewMode.List || viewMode == ListViewMode.Icons )
			{
				// Just show the first column
				AddChild( IconPanel );
				string text = (SubItems != null && SubItems.Count > 0) ? SubItems[0] : "";
				var itemtext = AddChild<Panel>( "listview-text" );
				itemtext.AddChild( new Label { Text = text } );
			}


			// Always update selected state
			SetSelected( IsSelected );
		}

		private void SelectSelf()
		{
			ParentListView.SelectItem( this );
		}

		private void OnItemDoubleClicked()
		{
			ParentListView.OnItemActivated?.Invoke( this );
		}

		public void SetSelected( bool selected )
		{
			IsSelected = selected;
			SetClass( "selected", selected );
		}

		public void BeginRename( Action<string> onRenameComplete )
		{
			// Remove existing label(s)
			foreach ( var child in Children.ToList() )
			{
				if ( child.HasClass( "listview-text" ) )
					child.Delete();
			}

			// Create and add TextEntry
			_renameEntry = new TextEntry
			{
				Text = (SubItems != null && SubItems.Count > 0) ? SubItems[0] : "",
				Style = { Width = Length.Percent( 100 ) }
			};
			AddChild( _renameEntry );
			_renameEntry.Focus();

			//_renameEntry.OnBlur += () => EndRename( onRenameComplete );
			//_renameEntry.OnEnterPressed += () => EndRename( onRenameComplete );
			_renameEntry.AddEventListener( "onblur", () => EndRename( onRenameComplete ) );
			_renameEntry.AddEventListener( "onsubmit", () => EndRename( onRenameComplete ) );
		}

		private void EndRename( Action<string> onRenameComplete )
		{
			if ( _renameEntry == null ) return;
			string newName = _renameEntry.Text;
			_renameEntry.Delete();
			_renameEntry = null;

			// Restore label
			string text = newName;
			var itemtext = AddChild<Panel>( "listview-text" );
			itemtext.AddChild( new Label { Text = text } );

			onRenameComplete?.Invoke( newName );
		}
		public class ItemDragEvent
		{
			/// <summary>
			/// For ondrag event - the delta of the mouse movement
			/// </summary>
			public Vector2 MouseDelta;

			/// <summary>
			/// The position on the Target panel where the drag started
			/// </summary>
			public Vector2 LocalGrabPosition;

			/// <summary>
			/// The position relative to the screen where the drag started
			/// </summary>
			public Vector2 ScreenGrabPosition;

			/// <summary>
			/// The current mouse position relative to target
			/// </summary>
			public Vector2 LocalPosition;

			/// <summary>
			/// The current position relative to the screen
			/// </summary>
			public Vector2 ScreenPosition;
		}
	}

	public Panel ItemContainer { get; set; }

	public List<ListViewColumn> Columns { get; } = new();
	public List<ListViewItem> Items { get; } = new();
	private ListViewMode _viewMode = ListViewMode.Details;
	public ListViewMode ViewMode
	{
		get => _viewMode;
		set
		{
			if ( _viewMode != value )
			{
				_viewMode = value;
				UpdateItems();
			}
		}
	}

	public Action<ListViewItem> OnItemSelected { get; set; }
	public Action<ListViewItem> OnItemActivated { get; set; }

	// Sorting properties
	private int _sortColumnIndex = -1;
	private bool _sortAscending = true;

	public ListView()
	{
		ItemContainer = new ScrollPanel();
		ItemContainer.AddClass( "listview-container" );
		AddClass( "listview" );
		InitializeHeader();
	}

	public void AddColumn( string header, string field, int width = 100 )
	{
		Columns.Add( new ListViewColumn { Header = header, Field = field, Width = width } );
		UpdateHeader();
	}

	public ListViewItem AddItem( object data, List<string> subItems )
	{
		var item = new ListViewItem( this, data, subItems );
		Items.Add( item );

		// If we have a sort column, resort the items
		if ( _sortColumnIndex >= 0 )
		{
			SortItems();
			UpdateItems();
		}
		else
		{
			// Add to the correct container based on view mode
			if ( ViewMode == ListViewMode.Icons && ItemContainer != null )
			{
				ItemContainer.AddChild( item );
			}
			else
			{
				ItemContainer.AddChild( item );
			}
		}
		return item;
	}

	/// <summary>
	/// Selects an item and triggers the selection event.
	/// </summary>
	public void SelectItem( ListViewItem item )
	{
		foreach ( var i in Items )
		{
			i.SetSelected( false );
		}

		item.SetSelected( true );
		OnItemSelected?.Invoke( item );
	}

	/// <summary>
	/// Sorts the ListView items by the specified column index.
	/// </summary>
	public void SortByColumn( int columnIndex )
	{
		// If clicking the same column, toggle direction
		if ( _sortColumnIndex == columnIndex )
		{
			_sortAscending = !_sortAscending;
		}
		else
		{
			_sortColumnIndex = columnIndex;
			_sortAscending = true;
		}

		// Sort the items
		SortItems();

		// Update header to show sort indicators
		UpdateHeader();

		// Refresh the view
		UpdateItems();
	}

	/// <summary>
	/// Sorts the items based on current sort settings.
	/// </summary>
	private void SortItems()
	{
		if ( _sortColumnIndex < 0 || _sortColumnIndex >= Columns.Count )
			return;

		// Create a temporary sorted list
		var sortedItems = new List<ListViewItem>();

		if ( _sortAscending )
		{
			sortedItems = Items
				.OrderBy( item =>
					item.SubItems != null && _sortColumnIndex < item.SubItems.Count
					? item.SubItems[_sortColumnIndex]
					: "" )
				.ToList();
		}
		else
		{
			sortedItems = Items
				.OrderByDescending( item =>
					item.SubItems != null && _sortColumnIndex < item.SubItems.Count
					? item.SubItems[_sortColumnIndex]
					: "" )
				.ToList();
		}

		// Replace items with sorted list
		Items.Clear();
		Items.AddRange( sortedItems );
	}

	/// <summary>
	/// Updates the header based on current columns.
	/// </summary>
	private void UpdateHeader()
	{
		// Find and remove the existing header if it exists
		var existingHeader = ChildrenOfType<Panel>().FirstOrDefault( p => p.HasClass( "listview-header" ) );
		existingHeader?.Delete();

		// Create a new header
		InitializeHeader();
	}

	/// <summary>
	/// Initializes the header with current columns.
	/// </summary>
	public void InitializeHeader()
	{
		if ( ViewMode == ListViewMode.Details && Columns.Any() )
		{
			var header = new Panel();
			header.AddClass( "listview-header" );

			for ( int i = 0; i < Columns.Count; i++ )
			{
				var col = Columns[i];
				var colPanel = new Panel();
				colPanel.AddClass( "listview-header-column" );
				colPanel.Style.Width = col.Width;

				// Make a row for the header text and sort indicator
				var headerRow = new Panel();
				headerRow.Style.FlexDirection = FlexDirection.Row;
				headerRow.Style.AlignItems = Align.Center;

				// Add the header text
				headerRow.AddChild( new Label { Text = col.Header } );

				// Add sort indicator if this is the sort column
				if ( _sortColumnIndex == i )
				{
					var sortIndicator = new Label
					{
						Text = _sortAscending ? "▲" : "▼",
						Style =
						{
							MarginLeft = 4,
							FontSize = 8
						}
					};
					sortIndicator.AddClass( "sort-indicator" );
					sortIndicator.AddClass( _sortAscending ? "sort-up" : "sort-down" );
					headerRow.AddChild( sortIndicator );
				}

				colPanel.AddChild( headerRow );

				// Add click handler for sorting (capture the index)
				int columnIndex = i; // Capture for closure
				colPanel.AddEventListener( "onclick", () => SortByColumn( columnIndex ) );

				header.AddChild( colPanel );
			}

			AddChild( header );
		}
	}

	public void ClearList()
	{
		foreach ( var child in Children.ToList() )
		{
			if ( child.ElementName == "element" ) continue;
			child.Parent = null;
		}
	}

	/// <summary>
	/// Updates all items to match the current view mode.
	/// </summary>
	public void UpdateItems()
	{
		ClearList();
		// Clear all children first
		// Re-add the header for details mode
		InitializeHeader();

		// Special handling for Icons view - use a container for grid layout
		if ( ViewMode == ListViewMode.Icons )
		{
			ItemContainer = new ScrollPanel();
			ItemContainer.AddClass( "listview-container" );
			ItemContainer.AddClass( "listview-icon-container" );
			AddChild( ItemContainer );

			// Update all items and add them to the container
			foreach ( var item in Items )
			{
				item.UpdateViewMode( ViewMode );
				ItemContainer.AddChild( item );
			}
		}
		else
		{
			ItemContainer = new ScrollPanel();
			ItemContainer.AddClass( "listview-container" );
			AddChild( ItemContainer );
			// For other views, add items directly to the ListView
			foreach ( var item in Items )
			{
				item.UpdateViewMode( ViewMode );
				ItemContainer.AddChild( item );
			}
		}
	}
}