Editor/Commands/SuiAlignElementsCommand.cs
using System.Collections.Generic;
using System.Linq;
using SboxUiDesigner.Runtime;

namespace SboxUiDesigner.EditorUi.Commands;

/// <summary>
/// Align multiple absolute-mode elements to a common edge or center.
///
/// Bounding box is computed from each element's Layout.X/Y/Width/Height
/// in their parent's coordinate space. For V1 this means alignment is only
/// meaningful when all selected elements share the same parent AND same
/// anchor — otherwise the X/Y values aren't comparable. The controller is
/// expected to filter the input set accordingly.
///
/// Single undo entry covers the move of every element.
/// </summary>
public sealed class SuiAlignElementsCommand : ISuiCommand
{
	public enum Mode
	{
		Left,
		HCenter,
		Right,
		Top,
		VCenter,
		Bottom,
	}

	private readonly List<string> _ids;
	private readonly Mode _mode;

	// Per-element undo state, captured during Apply.
	private readonly List<(string Id, float OldX, float OldY)> _saved = new();

	public string Description => $"Align {_mode}";

	public SuiAlignElementsCommand( IEnumerable<string> elementIds, Mode mode )
	{
		_ids = elementIds.ToList();
		_mode = mode;
	}

	public void Apply( SuiDocument doc )
	{
		if ( doc == null || _ids.Count < 2 ) return;
		_saved.Clear();

		var elements = _ids.Select( doc.GetElement ).Where( e => e?.Layout != null && e.Layout.Mode == SuiLayoutMode.Absolute ).ToList();
		if ( elements.Count < 2 ) return;

		// Bounding box — min/max of each element's own X/Y/Right/Bottom in its
		// parent-local coord space (per anchor). We restrict to single-parent
		// upstream so this is well-defined.
		var minX = elements.Min( e => e.Layout.X );
		var maxRight = elements.Max( e => e.Layout.X + e.Layout.Width );
		var minY = elements.Min( e => e.Layout.Y );
		var maxBottom = elements.Max( e => e.Layout.Y + e.Layout.Height );

		float centerX = (minX + maxRight) * 0.5f;
		float centerY = (minY + maxBottom) * 0.5f;

		foreach ( var el in elements )
		{
			_saved.Add( (el.Id, el.Layout.X, el.Layout.Y) );

			switch ( _mode )
			{
				case Mode.Left:    el.Layout.X = minX; break;
				case Mode.HCenter: el.Layout.X = centerX - el.Layout.Width * 0.5f; break;
				case Mode.Right:   el.Layout.X = maxRight - el.Layout.Width; break;
				case Mode.Top:     el.Layout.Y = minY; break;
				case Mode.VCenter: el.Layout.Y = centerY - el.Layout.Height * 0.5f; break;
				case Mode.Bottom:  el.Layout.Y = maxBottom - el.Layout.Height; break;
			}
		}
	}

	public void Undo( SuiDocument doc )
	{
		if ( doc == null ) return;
		foreach ( var s in _saved )
		{
			var el = doc.GetElement( s.Id );
			if ( el?.Layout == null ) continue;
			el.Layout.X = s.OldX;
			el.Layout.Y = s.OldY;
		}
		_saved.Clear();
	}
}