Editor/InteriorLayoutBuilder/RoomLayoutTool.Dimensions.cs
using System;
using System.Linq;

namespace ReusableRoomLayout;

public sealed partial class RoomLayoutTool
{
	public void SetSelectedDoorWidth( float width )
	{
		EnsureLayoutLoadedForControls();

		if ( SelectedDoor() is not { } door )
		{
			return;
		}

		width = Math.Clamp( width, 1.0f, MathF.Max( 1.0f, DoorWallLength( door ) ) );
		if ( Math.Abs( door.Width - width ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		door.Width = width;
		ClampDoorOffset( door );
		CommitLayoutChange();
	}

	public void SetSelectedDoorHeight( float height )
	{
		EnsureLayoutLoadedForControls();

		if ( SelectedDoor() is not { } door )
		{
			return;
		}

		height = Math.Clamp( height, 1.0f, MathF.Max( 1.0f, document.Settings.WallHeight ) );
		if ( Math.Abs( door.Height - height ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		door.Height = height;
		CommitLayoutChange();
	}

	public void SetSelectedWindowWidth( float width )
	{
		EnsureLayoutLoadedForControls();

		if ( SelectedWindow() is not { } window )
		{
			return;
		}

		width = Math.Clamp( width, 1.0f, MathF.Max( 1.0f, WindowWallLength( window ) ) );
		if ( Math.Abs( window.Width - width ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		window.Width = width;
		ClampWindowOffset( window );
		CommitLayoutChange();
	}

	public void SetSelectedWindowHeight( float height )
	{
		EnsureLayoutLoadedForControls();

		if ( SelectedWindow() is not { } window )
		{
			return;
		}

		var sillHeight = window.SillHeight >= 0.0f ? window.SillHeight : document.Settings.WindowSillHeight;
		sillHeight = Math.Clamp( sillHeight, 0.0f, MathF.Max( 0.0f, document.Settings.WallHeight - 1.0f ) );
		height = Math.Clamp( height, 1.0f, MathF.Max( 1.0f, document.Settings.WallHeight - sillHeight ) );
		if ( Math.Abs( window.Height - height ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		window.Height = height;
		CommitLayoutChange();
	}

	public void SetSelectedWindowSillHeight( float height )
	{
		EnsureLayoutLoadedForControls();

		if ( SelectedWindow() is not { } window )
		{
			return;
		}

		height = Math.Clamp( height, 0.0f, MathF.Max( 0.0f, document.Settings.WallHeight - 1.0f ) );
		if ( Math.Abs( window.SillHeight - height ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		window.SillHeight = height;
		window.Height = Math.Clamp( window.Height, 1.0f, MathF.Max( 1.0f, document.Settings.WallHeight - height ) );
		CommitLayoutChange();
	}

	private float DoorWallLength( RoomLayoutDoor door )
	{
		if ( door.CorridorId != 0 )
		{
			return CorridorSegmentLength( door.CorridorId, door.CorridorSegmentIndex );
		}

		var room = document.FindRoom( door.RoomId );
		return room is null ? 4096.0f : WallLength( room, door.Side );
	}

	private float CorridorSegmentLength( int corridorId, int segmentIndex )
	{
		var corridor = document.Corridors.FirstOrDefault( candidate => candidate.Id == corridorId );
		if ( corridor is not null &&
			TryGetCorridorPath( corridor, out var points ) &&
			segmentIndex >= 0 &&
			segmentIndex < points.Count - 1 )
		{
			var a = points[segmentIndex];
			var b = points[segmentIndex + 1];
			return MathF.Abs( b.x - a.x ) >= MathF.Abs( b.y - a.y )
				? MathF.Abs( b.x - a.x )
				: MathF.Abs( b.y - a.y );
		}

		return 4096.0f;
	}

	private void ClampDoorOffset( RoomLayoutDoor door )
	{
		var wallLength = DoorWallLength( door );
		var halfWidth = MathF.Max( 0.5f, door.Width * 0.5f );
		door.Offset = Math.Clamp( door.Offset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
	}

	private void ClampWindowOffset( RoomLayoutWindow window )
	{
		var wallLength = WindowWallLength( window );
		var halfWidth = MathF.Max( 0.5f, window.Width * 0.5f );
		window.Offset = Math.Clamp( window.Offset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
	}
}