Editor/Tileset/TilesetTools/Tools/LineTileTool.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;

namespace SpriteTools.TilesetTool.Tools;

/// <summary>
/// Used to paint tiles on the selected layer.
/// </summary>
[Title( "Line" )]
[Icon( "horizontal_rule" )]
[Alias( "tileset-tools.line-tool" )]
[Group( "1" )]
[Order( 0 )]
public class LineTileTool : BaseTileTool
{
	public LineTileTool ( TilesetTool parent ) : base( parent ) { }

	/// <summary>
	/// The distance/spacing between each tile in the line.
	/// </summary>
	[Group( "Line Tool" ), Property, Range( 0, 8 ), Step( 1 )] public int Separation { get; set; } = 0;

	Vector2 startPos;
	Vector2 lastTilePos;
	bool holding = false;

	public override void OnUpdate ()
	{
		if ( !CanUseTool() ) return;

		var pos = GetGizmoPos();
		Parent._sceneObject.Transform = new Transform( pos, Rotation.Identity, 1 );
		Parent._sceneObject.RenderingEnabled = true;

		var tilePos = ( pos - Parent.SelectedComponent.WorldPosition ) / Parent.SelectedLayer.TilesetResource.GetTileSize();

		if ( holding )
		{
			var positions = new List<Vector2>() { startPos - tilePos };

			float sep = 1f / ( Separation + 1f );
			var dx = tilePos.x - startPos.x;
			var dy = tilePos.y - startPos.y;
			dx *= sep;
			dy *= sep;
			var delta = Math.Max( Math.Abs( dx ), Math.Abs( dy ) );
			for ( int i = 0; i <= delta; i++ )
			{
				var x = startPos.x + i * dx / delta / sep;
				var y = startPos.y + i * dy / delta / sep;
				x = (int)Math.Round( x );
				y = (int)Math.Round( y );
				var thisPos = new Vector2( x, y ) - tilePos;
				if ( !positions.Contains( thisPos ) )
					positions.Add( thisPos );
			}
			if ( tilePos != lastTilePos )
			{
				UpdateTilePositions( positions );
				lastTilePos = tilePos;
			}

			if ( !Gizmo.IsLeftMouseDown )
			{
				holding = false;
				Parent._sceneObject.ClearPositions();

				var brush = AutotileBrush;
				var tile = TilesetTool.Active.SelectedTile;
				if ( brush is null )
				{
					foreach ( var ppos in positions )
					{
						Parent.PlaceTile( (Vector2Int)( tilePos + ppos ), tile.Id, Vector2Int.Zero, false );
					}
				}
				else
				{
					foreach ( var ppos in positions )
					{
						Parent.PlaceAutotile( brush.Id, (Vector2Int)( tilePos + ppos ) );
					}
				}
				Parent.SelectedComponent.IsDirty = true;
				_componentUndoScope?.Dispose();
				_componentUndoScope = null;
			}
		}
		else if ( Gizmo.IsLeftMouseDown )
		{
			if ( _componentUndoScope is null )
			{
				_componentUndoScope = SceneEditorSession.Active.UndoScope( "Paint Tiles" ).WithComponentChanges( Parent.SelectedComponent ).Push();
			}
			startPos = tilePos;
			holding = true;
		}
		else
		{
			if ( tilePos != lastTilePos )
			{
				UpdateTilePositions( new List<Vector2> { 0 } );
				lastTilePos = tilePos;
			}
		}

	}


	[Shortcut( "tileset-tools.line-tool", "l", typeof( SceneViewportWidget ) )]
	public static void ActivateSubTool ()
	{
		if ( EditorToolManager.CurrentModeName != nameof( TilesetTool ) ) return;
		EditorToolManager.SetSubTool( nameof( LineTileTool ) );
	}
}