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

namespace ReusableRoomLayout;

public sealed partial class RoomLayoutTool
{
	public int ActiveFloor => activeFloor;
	public string ActiveFloorName => FloorLabel( activeFloor );
	public float FloorSpacing => document.Settings.EffectiveFloorSpacing;
	public bool CanMoveSelectedToActiveFloor
	{
		get
		{
			var selectedFloor = SelectedFloor();
			return selectedFloor.HasValue && selectedFloor.Value != activeFloor;
		}
	}

	public void SetActiveFloor( int floor )
	{
		SetActiveFloor( floor, keepSelection: false );
	}

	public void StepActiveFloor( int delta )
	{
		SetActiveFloor( activeFloor + delta );
	}

	public void MoveSelectedToActiveFloor()
	{
		EnsureLayoutLoadedForControls();

		if ( !CanMoveSelectedToActiveFloor )
		{
			return;
		}

		PushLayoutUndo();

		if ( SelectedDoor() is { } selectedDoor )
		{
			SetParentFloor( selectedDoor, activeFloor );
		}
		else if ( SelectedWindow() is { } selectedWindow )
		{
			SetParentFloor( selectedWindow, activeFloor );
		}
		else if ( SelectedCorridor() is { } selectedCorridor )
		{
			SetCorridorFloorWithEndpointRooms( selectedCorridor, activeFloor );
		}
		else if ( document.FindRoom( selectedRoomId ) is { } selectedRoom )
		{
			selectedRoom.Floor = activeFloor;
		}
		else if ( SelectedFloorCutout() is { } selectedCutout )
		{
			selectedCutout.Floor = activeFloor;
		}

		CommitLayoutChange();
	}

	public void SetFloorSpacing( float spacing )
	{
		EnsureLayoutLoadedForControls();

		spacing = Math.Clamp( spacing, 0.0f, 8192.0f );
		if ( Math.Abs( document.Settings.FloorSpacing - spacing ) < 0.001f )
		{
			return;
		}

		PushLayoutUndo();
		document.Settings.FloorSpacing = spacing;
		CommitLayoutChange();
	}

	private void SetActiveFloorFromSelection()
	{
		var selectedFloor = SelectedFloor();
		if ( selectedFloor.HasValue )
		{
			SetActiveFloor( selectedFloor.Value, keepSelection: true );
		}
	}

	private void SetActiveFloor( int floor, bool keepSelection )
	{
		floor = RoomLayoutDocument.ClampFloor( floor );
		if ( activeFloor == floor )
		{
			return;
		}

		CancelLayoutInteraction( restoreActiveEdit: true );
		activeFloor = floor;

		if ( !keepSelection )
		{
			ClearSelectionOutsideActiveFloor();
		}
	}

	private void ClearSelectionOutsideActiveFloor()
	{
		if ( SelectedFloor() == activeFloor )
		{
			return;
		}

		selectedRoomId = 0;
		selectedDoorId = 0;
		selectedWindowId = 0;
		selectedCorridorId = 0;
		selectedFloorCutoutId = 0;
	}

	private int? SelectedFloor()
	{
		if ( SelectedDoor() is { } door )
		{
			return document.FloorFor( door );
		}

		if ( SelectedWindow() is { } window )
		{
			return document.FloorFor( window );
		}

		if ( SelectedCorridor() is { } corridor )
		{
			return document.FloorFor( corridor );
		}

		if ( document.FindRoom( selectedRoomId ) is { } room )
		{
			return document.FloorFor( room );
		}

		if ( SelectedFloorCutout() is { } cutout )
		{
			return document.FloorFor( cutout );
		}

		return null;
	}

	private void SetParentFloor( RoomLayoutDoor door, int floor )
	{
		if ( door.RoomId != 0 && document.FindRoom( door.RoomId ) is { } room )
		{
			room.Floor = floor;
			return;
		}

		if ( door.CorridorId != 0 && document.Corridors.FirstOrDefault( corridor => corridor.Id == door.CorridorId ) is { } corridor )
		{
			corridor.Floor = floor;
		}
	}

	private void SetCorridorFloorWithEndpointRooms( RoomLayoutCorridor corridor, int floor )
	{
		corridor.Floor = floor;
		SetDoorRoomFloor( corridor.StartDoorId, floor );
		SetDoorRoomFloor( corridor.EndDoorId, floor );
	}

	private void SetDoorRoomFloor( int doorId, int floor )
	{
		var door = document.FindDoor( doorId );
		if ( door is not null && door.RoomId != 0 && document.FindRoom( door.RoomId ) is { } room )
		{
			room.Floor = floor;
		}
	}

	private void SetParentFloor( RoomLayoutWindow window, int floor )
	{
		if ( window.RoomId != 0 && document.FindRoom( window.RoomId ) is { } room )
		{
			room.Floor = floor;
			return;
		}

		if ( window.CorridorId != 0 && document.Corridors.FirstOrDefault( corridor => corridor.Id == window.CorridorId ) is { } corridor )
		{
			corridor.Floor = floor;
		}
	}

	private bool IsActiveFloor( RoomLayoutRoom room )
	{
		return document.FloorFor( room ) == activeFloor;
	}

	private bool IsActiveFloor( RoomLayoutDoor door )
	{
		return document.FloorFor( door ) == activeFloor;
	}

	private bool IsActiveFloor( RoomLayoutWindow window )
	{
		return document.FloorFor( window ) == activeFloor;
	}

	private bool IsActiveFloor( RoomLayoutCorridor corridor )
	{
		return document.FloorFor( corridor ) == activeFloor;
	}

	private bool IsActiveFloor( RoomLayoutFloorCutout cutout )
	{
		return document.FloorFor( cutout ) == activeFloor;
	}

	private float ActiveFloorZ()
	{
		return FloorWorldZ( activeFloor );
	}

	private float FloorWorldZ( int floor )
	{
		return document.Settings.EffectiveFloorSpacing * floor;
	}

	private static string FloorLabel( int floor )
	{
		return floor >= 0
			? $"Floor {floor}"
			: $"Basement {Math.Abs( floor )}";
	}
}