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

namespace ReusableRoomLayout;

public sealed partial class RoomLayoutTool
{
	private RoomLayoutCorridor SelectedCorridor()
	{
		return selectedCorridorId == 0
			? null
			: document.Corridors.FirstOrDefault( corridor => corridor.Id == selectedCorridorId );
	}

	private float CorridorClearWidth( RoomLayoutCorridor corridor )
	{
		var requested = corridor.Width <= 0.0f
			? document.Settings.DefaultCorridorWidth
			: corridor.Width;

		return CorridorWidthForDoors( corridor.StartDoorId, corridor.EndDoorId, requested );
	}

	private void SetCorridorWidth( RoomLayoutCorridor corridor, float width )
	{
		var next = Math.Clamp( width, MathF.Max( 16.0f, document.Settings.WallThickness * 2.0f ), MaxCorridorWidth( corridor ) );
		corridor.Width = SnapWidth( next );
		ExpandDoorForCorridor( corridor.StartDoorId, corridor.Width );
		ExpandDoorForCorridor( corridor.EndDoorId, corridor.Width );
	}

	private float CorridorWidthForDoors( int startDoorId, int endDoorId )
	{
		return CorridorWidthForDoors( startDoorId, endDoorId, document.Settings.DefaultCorridorWidth );
	}

	private float CorridorWidthForDoors( int startDoorId, int endDoorId, float requestedWidth )
	{
		var width = MathF.Max( 1.0f, requestedWidth );
		var startDoor = document.FindDoor( startDoorId );
		var endDoor = document.FindDoor( endDoorId );

		if ( startDoor is not null && startDoor.Width > 0.0f )
		{
			width = MathF.Min( width, startDoor.Width );
		}

		if ( endDoor is not null && endDoor.Width > 0.0f )
		{
			width = MathF.Min( width, endDoor.Width );
		}

		return width;
	}

	private float CorridorWidthStep()
	{
		return MathF.Max( 8.0f, document.Settings.GridSize * 0.25f );
	}

	private float SnapWidth( float width )
	{
		var step = CorridorWidthStep();
		return MathF.Round( width / step ) * step;
	}

	private float MaxCorridorWidth( RoomLayoutCorridor corridor )
	{
		var maxWidth = 512.0f;
		maxWidth = MathF.Min( maxWidth, MaxDoorWidth( corridor.StartDoorId ) );
		maxWidth = MathF.Min( maxWidth, MaxDoorWidth( corridor.EndDoorId ) );
		return MathF.Max( MathF.Max( 16.0f, document.Settings.WallThickness * 2.0f ), maxWidth );
	}

	private float MaxDoorWidth( int doorId )
	{
		var door = document.FindDoor( doorId );
		var room = door is null ? null : document.FindRoom( door.RoomId );
		if ( door is null || room is null )
		{
			return 512.0f;
		}

		return door.Side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
			? room.Bounds.Width
			: room.Bounds.Height;
	}

	private void ExpandDoorForCorridor( int doorId, float width )
	{
		var door = document.FindDoor( doorId );
		var room = door is null ? null : document.FindRoom( door.RoomId );
		if ( door is null || room is null || door.Width >= width )
		{
			return;
		}

		var wallLength = door.Side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
			? room.Bounds.Width
			: room.Bounds.Height;
		var nextWidth = Math.Clamp( width, MathF.Max( 16.0f, document.Settings.WallThickness * 2.0f ), wallLength );
		var halfWidth = nextWidth * 0.5f;

		door.Width = nextWidth;
		door.Offset = Math.Clamp( door.Offset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
	}
}