Editor/Tileset/TilesetTools/TileControl/TilesetTileControl.cs
using Editor;
using Sandbox;
using System.Collections.Generic;
using System.Linq;

namespace SpriteTools.TilesetEditor;

public class TilesetTileControl : Widget
{
	TilesetTileListControl ParentList;
	internal TilesetResource.Tile Tile;

	internal LabelTextEntry labelText;

	Drag dragData;
	bool draggingAbove = false;
	bool draggingBelow = false;

	public TilesetTileControl ( TilesetTileListControl list, TilesetResource.Tile tile )
	{
		ParentList = list;
		Tile = tile;

		MouseClick = () =>
		{
			ParentList.SelectTile( this, Tile );
		};

		VerticalSizeMode = SizeMode.Flexible;

		StatusTip = $"Select Tile \"{Tile?.GetName()}\"";
		Cursor = CursorShape.Finger;

		Layout = Layout.Row();
		Layout.AddSpacingCell( 20 );
		Layout.Margin = 4;
		Layout.Spacing = 4;

		var serializedObject = Tile.GetSerialized();
		serializedObject.TryGetProperty( nameof( TilesetResource.Tile.Name ), out var name );
		labelText = new LabelTextEntry( name );
		labelText.EmptyValue = $"Tile {tile.Position}";
		Layout.Add( labelText );

		IsDraggable = true;
		AcceptDrops = true;
	}

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

		ParentList.Selected.Remove( this );
	}

	protected override void OnPaint ()
	{
		if ( dragData?.IsValid ?? false )
		{
			Paint.SetBrushAndPen( Theme.WindowBackground.WithAlpha( 0.5f ) );
			Paint.DrawRect( LocalRect, 4 );
		}
		else if ( ParentList.Selected.Contains( this ) )
		{
			Paint.SetBrushAndPen( Theme.Highlight.Darken( 0.5f ) );
			Paint.DrawRect( LocalRect, 4 );
		}
		else if ( IsUnderMouse )
		{
			Paint.SetBrushAndPen( Theme.Text.WithAlpha( 0.1f ) );
			Paint.DrawRect( LocalRect, 4 );
		}

		// if (Pixmap is null) LoadPixmap();
		// if (Pixmap is not null)
		// {
		// 	var pixRect = Rect.FromPoints(LocalRect.TopLeft, LocalRect.TopLeft + new Vector2(16, 16));
		// 	pixRect.Position = pixRect.Position + new Vector2(3, LocalRect.Height / 2 - 7);
		// 	Paint.Draw(pixRect, Pixmap);
		// }

		var atlas = TileAtlas.FromTileset( Tile.Tileset );
		if ( atlas is not null )
		{
			var texture = atlas.GetTextureFromCell( Tile.Position );
			var pixRect = Rect.FromPoints( LocalRect.TopLeft, LocalRect.TopLeft + new Vector2( 16, 16 ) );
			pixRect.Position = pixRect.Position + new Vector2( 3, LocalRect.Height / 2 - 7 );
			var pixmap = Pixmap.FromTexture( texture );
			Paint.Draw( pixRect, pixmap );
		}

		if ( draggingAbove )
		{
			Paint.SetPen( Theme.Highlight, 2f, PenStyle.Dot );
			Paint.DrawLine( LocalRect.TopLeft, LocalRect.TopRight );
			draggingAbove = false;
		}
		else if ( draggingBelow )
		{
			Paint.SetPen( Theme.Highlight, 2f, PenStyle.Dot );
			Paint.DrawLine( LocalRect.BottomLeft, LocalRect.BottomRight );
			draggingBelow = false;
		}
	}

	void Rename ()
	{
		labelText.Edit();
	}

	void DeleteLayerPopup ()
	{
		var popup = new PopupWidget( ParentList );
		popup.Layout = Layout.Column();
		popup.Layout.Margin = 16;
		popup.Layout.Spacing = 8;

		popup.Layout.Add( new Label( $"Are you sure you want to delete this Layer?" ) );

		var button = new Button.Primary( "Delete" );


		button.MouseClick = () =>
		{
			Delete();
			popup.Visible = false;
		};

		popup.Layout.Add( button );

		var bottomBar = popup.Layout.AddRow();
		bottomBar.AddStretchCell();
		bottomBar.Add( button );

		var popupPos = new Vector2( Editor.Application.CursorPosition.x - 250, Editor.Application.CursorPosition.y );
		popup.Position = popupPos;
		popup.Visible = true;
	}

	void Delete ()
	{
		if ( ParentList.Selected.Contains( this ) )
		{
			if ( ParentList.Selected.Count == ParentList.Buttons.Count )
			{
				ParentList.DeleteAll();
			}
			else
			{
				foreach ( var selected in ParentList.Selected )
				{
					ParentList.DeleteTile( selected.Tile );
				}
			}
		}
		else
		{
			ParentList.DeleteTile( Tile );
		}
	}

	protected override void OnContextMenu ( ContextMenuEvent e )
	{
		base.OnContextMenu( e );

		var m = new Menu( this );

		m.AddOption( "Rename", "edit", Rename );
		m.AddOption( "Delete", "delete", Delete );

		m.OpenAtCursor( false );
	}

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

		dragData = new Drag( this );
		dragData.Data.Object = Tile;
		dragData.Execute();
	}

	public override void OnDragHover ( DragEvent ev )
	{
		base.OnDragHover( ev );

		if ( !TryDragOperation( ev, out var dragDelta ) )
		{
			draggingAbove = false;
			draggingBelow = false;
			return;
		}

		draggingAbove = dragDelta > 0;
		draggingBelow = dragDelta < 0;
	}

	public override void OnDragDrop ( DragEvent ev )
	{
		base.OnDragDrop( ev );

		if ( !TryDragOperation( ev, out var delta ) ) return;

		var list = ParentList.SerializedProperty.GetValue<List<TilesetResource.Tile>>();
		var index = list.IndexOf( Tile );
		var movingIndex = index + delta;
		var layer = list[movingIndex];

		list.RemoveAt( movingIndex );
		list.Insert( index, layer );

		ParentList.SerializedProperty.SetValue( list );
		ParentList.UpdateList();
	}

	bool TryDragOperation ( DragEvent ev, out int delta )
	{
		delta = 0;
		var tile = ev.Data.OfType<TilesetResource.Tile>().FirstOrDefault();

		if ( tile == null || ParentList == null ) return false;

		var layerList = ParentList.SerializedProperty.GetValue<List<TilesetResource.Tile>>();
		var myIndex = layerList.IndexOf( Tile );
		var otherIndex = layerList.IndexOf( tile );
		if ( myIndex == -1 || otherIndex == -1 ) return false;

		delta = otherIndex - myIndex;
		return true;
	}
}