Editor/Widgets/Table.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Editor;
using Sandbox;
using Sandbox.UI;
using Sandbox.VR;
using Label = Editor.Label;

namespace DataTablesEditor;

/// <summary>
/// I am absolutely not proud of this at all.
/// </summary>
internal class TableView : Widget
{
	public ListView ListView { get; set; }

	public List<Column> Columns { get; set; } = new();

	TableHeader Header;

	public Action<object> ItemClicked;

	public Dictionary<VirtualWidget, int> WidgetIndexMap = new();

	public TableView( Widget parent ) : base( parent )
	{
		ListView = new( this );

		ListView.ItemPaint = widget => PaintRow( widget );
		ListView.ItemSize = new Vector2( 0, 24 );
		ListView.ItemSpacing = 0;
		ListView.ItemClicked = o =>
		{
			if ( ItemClicked is not null )
				ItemClicked( o );
		};
		ListView.Margin = new Margin( 0, 4, 0, 0 );
		ListView.MultiSelect = true;

		Layout = Layout.Column();
		Layout.Add( Header = new TableHeader( this ) );
		Layout.Add( ListView );
	}

	public Column AddColumn( string name = null, int? width = null, Func<object, string> value = null )
	{
		var col = new Column();
		col.Name = name;
		col.Width = width ?? 50;
		col.Value = value;

		Columns.Add( col );

		return col;
	}

	protected override void DoLayout()
	{
		base.DoLayout();

		int i = 0;
		foreach ( var lbl in Header.Labels )
		{
			Columns[i].Width = (int)lbl.Width;
			i++;
		}
	}

	public void FillHeader()
	{
		foreach ( var column in Columns )
		{
			Header.AddColumn( column );
		}
	}

	public void SetItems<T>( IEnumerable<T> items )
	{
		foreach ( var elem in items.ToList().Cast<object>() )
		{
			ListView.AddItem( elem );
		}
	}

	public void AddItem( object item )
	{
		ListView.AddItem( item );
	}

	private void PaintRow( VirtualWidget widget )
	{
		/*if ( widget.Object is not T t )
			return;*/

		var isAlt = widget.Row % 2 == 0;
		var backgroundColor = widget.Selected
			? Theme.Selection
			: (widget.Hovered ? Theme.ButtonDefault.WithAlpha( 0.7f ) :
				isAlt ? Color.Parse( "#262627" ).GetValueOrDefault() : Color.Parse( "#313131" ).GetValueOrDefault());

		Paint.ClearPen();
		Paint.SetBrush( backgroundColor );
		Paint.DrawRect( widget.Rect );

		Rect rect = widget.Rect;

		Paint.SetDefaultFont();

		foreach ( var column in Columns )
		{
			Paint.SetPen( widget.Selected ? Color.White : column.TextColor );
			var width = column.Width + 4;
			rect.Width = width;
			Paint.DrawText( rect.Shrink( 8, 0 ), column.Value(widget.Object), column.TextFlag | TextFlag.CenterVertically | TextFlag.SingleLine );
			rect.Left += width;

			Paint.SetPen( Color.Parse("#414141").GetValueOrDefault() );
			Paint.DrawLine( rect.TopLeft.WithX( rect.TopLeft.x - 1 ), rect.BottomLeft.WithX( rect.BottomLeft.x - 1 ) );
			Paint.SetPen( widget.Selected ? Color.White : Theme.ControlText );
		}

		Paint.SetPen( Color.Parse("#414141").GetValueOrDefault() );
		Paint.DrawLine( widget.Rect.BottomLeft, widget.Rect.BottomRight );
	}

	protected override void OnPaint()
	{
		base.OnPaint();

		Paint.ClearPen();
		Paint.SetBrush( Theme.WidgetBackground.WithAlpha( 0.5f ) );
		Paint.DrawRect( LocalRect );

		Paint.ClearPen();
		Paint.SetBrush( Theme.WindowBackground );
		Paint.DrawRect( LocalRect.Shrink( 0, 0, 0, LocalRect.Height - 30 ) );
	}
}

internal class Column
{
	private string _name;
	public string Name
	{
		get
		{
			return _name;
		}
		set
		{
			if ( value is null )
				_name = null;
			else
			{
				StringBuilder result = new StringBuilder();
				result.Append( value[0] );

				for (int i = 1; i < value.Length; i++)
				{
					if ( char.IsUpper( value[i] ) && !char.IsUpper( value[i - 1] ) )
						result.Append( ' ' );

					result.Append( value[i] );
				}

				_name = result.ToString();
			}
		}
	}
	public int Width;
	public Func<object, string> Value;
	public TextFlag TextFlag = TextFlag.Left;
	public Label Label;
	public Color TextColor = Theme.ControlText;
}

internal class TableHeader : Widget
{
	readonly TableView Table;

	public List<Label> Labels = new();

	public HeaderSplitter _splitter;

	private static List<TableHeader> _headers;

	static TableHeader()
	{
		_headers = new();
	}

	[EditorEvent.Frame]
	public static void Frame()
	{
		foreach ( var Header in _headers )
		{
			if ( Header is null || !Header.IsValid )
				continue;

			var list = Header._splitter.Labels;
			for ( int i = 0; i < list.Count; i++ )
			{
				Header.Table.Columns[i].Width = (int)list[i].Width;
				Header.Table.ListView.Update();
			}
		}
	}

	public TableHeader( TableView parent ) : base( parent )
	{
		Table = parent;
		MinimumHeight = 25;

		Layout = Layout.Row();

		_splitter = new(this);
		_splitter.IsHorizontal = true;

		_headers.Add( this );

		Layout.Add( _splitter );
	}

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

		_headers.Remove( this );
	}

	public void AddColumn( Column column )
	{
		if ( _splitter.IsValid() )
			Labels.Add( _splitter.AddColumn( column ) );
	}
}

internal class HeaderSplitter : Splitter
{
	public List<Label> Labels = new();

	public HeaderSplitter(Widget parent) : base(parent)
	{
	}

	private int splitterCount = 0;
	public Label AddColumn( Column column )
	{
		var lbl = new Label( column.Name );
		lbl.ContentMargins = new Margin( 8, 0, 0, 0 );
		lbl.SetStyles( "font-weight: bold; font-size: 12px;" );
		lbl.OnPaintOverride = () =>
		{
			Paint.SetBrush( ControlWidget.ControlColor );
			Paint.SetPen( Color.Transparent );
			Paint.DrawRect( lbl.LocalRect );

			return false;
		};
		column.Label = lbl;
		Labels.Add( lbl );

		AddWidget( lbl );
		SetCollapsible( splitterCount++, false );

		return lbl;
	}
}