Editor/InteriorLayoutBuilder/RoomLayoutControls.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Editor;
using Sandbox;

namespace ReusableRoomLayout;

internal sealed partial class RoomLayoutControls : Widget
{
	private const int ModeButtonWidth = 132;
	private const int ActionButtonWidth = 132;
	private const int WideActionButtonWidth = 148;
	private const int DefaultMaterialsButtonWidth = 156;
	private const int SmallActionButtonWidth = 96;
	private const int DangerButtonWidth = 132;
	private const int FieldLabelWidth = 112;
	private const int LongFieldLabelWidth = 156;
	private const int NumberEditWidth = 96;
	private const int CheckboxWidth = 24;
	private const int MaterialLabelWidth = 100;
	private const int MaterialButtonWidth = 260;
	private const int MaterialScaleLabelWidth = 42;
	private const int MaterialScaleEditWidth = 72;
	private const int SelectedLabelWidth = 260;
	private const int SectionPaddingY = 20;
	private const int SectionSpacing = 8;

	private readonly RoomLayoutTool fixedTool;
	private readonly Dictionary<MaterialButtonKey, Button> materialButtons = new();
	private readonly Dictionary<MaterialButtonKey, LineEdit> materialScaleEdits = new();
	private SettingsTab activeSettingsTab = SettingsTab.Wall;
	private SegmentedControl settingsTabs;
	private Widget wallSettingsPanel;
	private Widget doorSettingsPanel;
	private Widget windowSettingsPanel;
	private Widget baseboardSettingsPanel;
	private Widget thresholdSettingsPanel;
	private Widget roofSettingsPanel;
	private Widget defaultMaterialsPanel;
	private Widget selectedDimensionsLabel;
	private Widget selectedDoorDimensionsPanel;
	private Widget selectedWindowDimensionsPanel;
	private Label mode;
	private Label selectedLayout;
	private Button selectLayoutButton;
	private LineEdit wallHeightEdit;
	private LineEdit doorWidthEdit;
	private LineEdit doorHeightEdit;
	private LineEdit doorFrameThicknessEdit;
	private LineEdit windowWidthEdit;
	private LineEdit windowHeightEdit;
	private LineEdit windowSillHeightEdit;
	private LineEdit selectedDoorWidthEdit;
	private LineEdit selectedDoorHeightEdit;
	private LineEdit selectedWindowWidthEdit;
	private LineEdit selectedWindowHeightEdit;
	private LineEdit selectedWindowSillHeightEdit;
	private Checkbox baseboardsCheckbox;
	private Checkbox thresholdsCheckbox;
	private Checkbox roofCheckbox;
	private LineEdit baseboardHeightEdit;
	private LineEdit baseboardDepthEdit;
	private LineEdit thresholdDepthEdit;
	private LineEdit roofThicknessEdit;
	private Button deleteSelectedButton;
	private Button resetLayoutButton;
	private Button defaultMaterialsButton;

	public RoomLayoutControls( Widget parent ) : base( parent )
	{
		Build();
	}

	public RoomLayoutControls( Widget parent, RoomLayoutTool tool ) : base( parent )
	{
		fixedTool = tool;
		Build();
	}

	private void Build()
	{
		Layout = Layout.Column();
		Layout.Margin = 0;

		var scrollArea = Layout.Add( new ScrollArea( this ), 1 );
		scrollArea.Canvas = new Widget( scrollArea );
		scrollArea.Canvas.SetSizeMode( SizeMode.Flexible, SizeMode.CanShrink );
		scrollArea.SetStyles( "QScrollArea { border: 0px; }" );

		var content = scrollArea.Canvas;
		content.Layout = Layout.Column();
		content.Layout.Alignment = TextFlag.LeftTop;
		content.Layout.Margin = 10;
		content.Layout.Spacing = 8;

		var modeSection = AddSection( content, "Modes", contentRows: 3 );
		mode = AddStatusLabel( modeSection );

		var modeRow = AddRow( modeSection );
		AddButton( modeRow, "Select", "ads_click", OnSelectClicked, ModeButtonWidth );
		AddButton( modeRow, "Create Room", "crop_square", OnRoomsClicked, ModeButtonWidth );
		AddButton( modeRow, "Create Door", "door_front", OnDoorsClicked, ModeButtonWidth );

		var modeRow2 = AddRow( modeSection );
		AddButton( modeRow2, "Create Window", "window", OnWindowsClicked, ModeButtonWidth );
		AddButton( modeRow2, "Create Corridor", "timeline", OnCorridorsClicked, ModeButtonWidth );
		AddButton( modeRow2, "Create Cutout", "stairs", OnFloorCutoutsClicked, ModeButtonWidth );

		AddFloorSection( AddSection( content, "Floors", contentRows: 2 ) );

		var actionSection = AddSection( content, "Actions", contentRows: 1 );
		var buildRow = AddRow( actionSection );
		AddButton( buildRow, "Rebuild", "construction", OnBuildClicked, ActionButtonWidth );
		AddButton( buildRow, "Clear Generated", "layers_clear", OnClearGeneratedClicked, WideActionButtonWidth );
		defaultMaterialsButton = AddButton( buildRow, "Default Materials", "tune", ToggleDefaultMaterialsPanel, DefaultMaterialsButtonWidth );
		defaultMaterialsButton.ToolTip = "Change inherited material defaults.";

		AddDefaultMaterialsSection( content );
		AddSettingsTabsSection( AddSection( content, "Settings", contentRows: 4 ) );
		AddSelectedSection( AddSection( content, "Selected", contentRows: 14 ) );
		AddResetLayoutSection( AddSection( content, "Layout", contentRows: 1 ) );
	}

	[EditorEvent.Frame]
	public void Frame()
	{
		var tool = GetTool();
		tool?.EnsureLayoutLoadedForControls();

		mode.Text = tool is null ? "Tool inactive" : $"Active: {tool.ModeName}";

		selectedLayout.Text = tool is null ? "Selected: none" : $"Selected: {tool.SelectedLayoutName}";
		if ( deleteSelectedButton is not null )
		{
			deleteSelectedButton.Enabled = tool?.HasSelectedLayout ?? false;
		}

		if ( selectLayoutButton is not null )
		{
			selectLayoutButton.Enabled = tool?.HasSelectableLayoutItems ?? false;
		}

		if ( defaultMaterialsButton is not null )
		{
			defaultMaterialsButton.Enabled = tool is not null;
			defaultMaterialsButton.Text = defaultMaterialsPanel?.Visible == true ? "Hide Defaults" : "Default Materials";
		}

		if ( resetLayoutButton is not null )
		{
			resetLayoutButton.Enabled = tool is not null;
		}

		UpdateFloorControls( tool );

		foreach ( var entry in materialButtons )
		{
			var key = entry.Key;
			var button = entry.Value;
			var thresholdDisabled = key.Slot == RoomLayoutMaterialSlot.Threshold && tool?.ThresholdsEnabled == false;
			var roofDisabled = key.Slot == RoomLayoutMaterialSlot.Roof && tool?.RoofEnabled == false;
			var enabled = tool is not null &&
				!thresholdDisabled &&
				!roofDisabled &&
				(key.Scope == MaterialScope.Defaults || tool.CanSetSelectedMaterialSlot( key.Slot ));
			var path = tool is null ? "" : MaterialPathForButton( tool, key );

			button.Enabled = enabled;
			button.Text = MaterialButtonText( path, key.Scope == MaterialScope.Selected );
			button.ToolTip = thresholdDisabled
				? "Enable thresholds in the Thresholds tab to use this material."
				: roofDisabled
					? "Enable roof in the Roof tab to use this material."
				: MaterialButtonTooltip( path, key.Scope == MaterialScope.Selected );
		}

		foreach ( var entry in materialScaleEdits )
		{
			UpdateMaterialScaleEdit( entry.Key, entry.Value, tool );
		}

		UpdateFloatEdit( wallHeightEdit, tool, tool?.WallHeight );
		UpdateFloatEdit( doorWidthEdit, tool, tool?.DoorWidth );
		UpdateFloatEdit( doorHeightEdit, tool, tool?.DoorHeight );
		UpdateFloatEdit( doorFrameThicknessEdit, tool, tool?.DoorFrameThickness );
		UpdateFloatEdit( windowWidthEdit, tool, tool?.WindowWidth );
		UpdateFloatEdit( windowHeightEdit, tool, tool?.WindowHeight );
		UpdateFloatEdit( windowSillHeightEdit, tool, tool?.WindowSillHeight );
		UpdateFloatEdit( windowFrameThicknessEdit, tool, tool?.WindowFrameThickness );
		UpdateFloatEdit( selectedDoorWidthEdit, tool, tool?.SelectedDoorWidth );
		UpdateFloatEdit( selectedDoorHeightEdit, tool, tool?.SelectedDoorHeight );
		UpdateFloatEdit( selectedWindowWidthEdit, tool, tool?.SelectedWindowWidth );
		UpdateFloatEdit( selectedWindowHeightEdit, tool, tool?.SelectedWindowHeight );
		UpdateFloatEdit( selectedWindowSillHeightEdit, tool, tool?.SelectedWindowSillHeight );
		UpdateCheckbox( baseboardsCheckbox, tool, tool?.BaseboardsEnabled );
		UpdateCheckbox( thresholdsCheckbox, tool, tool?.ThresholdsEnabled );
		UpdateCheckbox( roofCheckbox, tool, tool?.RoofEnabled );
		UpdateFloatEdit( baseboardHeightEdit, tool, tool?.BaseboardHeight );
		UpdateFloatEdit( baseboardDepthEdit, tool, tool?.BaseboardDepth );
		UpdateFloatEdit( thresholdDepthEdit, tool, tool?.ThresholdDepth );
		UpdateFloatEdit( roofThicknessEdit, tool, tool?.RoofThickness );
		SetFloatEditEditable( baseboardHeightEdit, tool is not null && tool.BaseboardsEnabled );
		SetFloatEditEditable( baseboardDepthEdit, tool is not null && tool.BaseboardsEnabled );
		SetFloatEditEditable( thresholdDepthEdit, tool is not null && tool.ThresholdsEnabled );
		SetFloatEditEditable( roofThicknessEdit, tool is not null && tool.RoofEnabled );
		SetFloatEditEditable( selectedDoorWidthEdit, tool?.HasSelectedDoor == true );
		SetFloatEditEditable( selectedDoorHeightEdit, tool?.HasSelectedDoor == true );
		SetFloatEditEditable( selectedWindowWidthEdit, tool?.HasSelectedWindow == true );
		SetFloatEditEditable( selectedWindowHeightEdit, tool?.HasSelectedWindow == true );
		SetFloatEditEditable( selectedWindowSillHeightEdit, tool?.HasSelectedWindow == true );

		var showSelectedDimensions = tool?.HasSelectedDoor == true || tool?.HasSelectedWindow == true;
		if ( selectedDimensionsLabel is not null )
		{
			selectedDimensionsLabel.Visible = showSelectedDimensions;
		}

		if ( selectedDoorDimensionsPanel is not null )
		{
			selectedDoorDimensionsPanel.Visible = tool?.HasSelectedDoor == true;
		}

		if ( selectedWindowDimensionsPanel is not null )
		{
			selectedWindowDimensionsPanel.Visible = tool?.HasSelectedWindow == true;
		}
	}

	private static void UpdateCheckbox( Checkbox checkbox, RoomLayoutTool tool, bool? value )
	{
		if ( checkbox is null )
		{
			return;
		}

		checkbox.Enabled = tool is not null;
		if ( tool is not null && value.HasValue )
		{
			checkbox.State = value.Value ? CheckState.On : CheckState.Off;
		}
	}

	private static void UpdateFloatEdit( LineEdit edit, RoomLayoutTool tool, float? value )
	{
		if ( edit is null )
		{
			return;
		}

		edit.ReadOnly = tool is null;
		if ( tool is not null && value.HasValue && !edit.IsFocused )
		{
			edit.Value = value.Value.ToString( "0.###", CultureInfo.InvariantCulture );
		}
	}

	private static void UpdateMaterialScaleEdit( MaterialButtonKey key, LineEdit edit, RoomLayoutTool tool )
	{
		if ( edit is null )
		{
			return;
		}

		var thresholdDisabled = key.Slot == RoomLayoutMaterialSlot.Threshold && tool?.ThresholdsEnabled == false;
		var roofDisabled = key.Slot == RoomLayoutMaterialSlot.Roof && tool?.RoofEnabled == false;
		var enabled = tool is not null &&
			!thresholdDisabled &&
			!roofDisabled &&
			(key.Scope == MaterialScope.Defaults || tool.CanSetSelectedMaterialSlot( key.Slot ));
		edit.ReadOnly = !enabled;

		if ( tool is null || edit.IsFocused )
		{
			return;
		}

		if ( key.Scope == MaterialScope.Defaults )
		{
			edit.PlaceholderText = "128";
			edit.Value = tool.GetMaterialScale( key.Slot ).ToString( "0.###", CultureInfo.InvariantCulture );
			edit.ToolTip = "World units before this material repeats.";
			return;
		}

		var selectedScale = tool.GetSelectedMaterialScale( key.Slot );
		var inheritedScale = tool.GetEffectiveSelectedMaterialScale( key.Slot );
		edit.PlaceholderText = inheritedScale.ToString( "0.###", CultureInfo.InvariantCulture );
		edit.Value = selectedScale > 0.0f
			? selectedScale.ToString( "0.###", CultureInfo.InvariantCulture )
			: "";
		edit.ToolTip = selectedScale > 0.0f
			? "World units before this material repeats."
			: "Clear to inherit the default material scale.";
	}

	private static void SetFloatEditEditable( LineEdit edit, bool editable )
	{
		if ( edit is not null )
		{
			edit.ReadOnly = !editable;
		}
	}

	private RoomLayoutTool GetTool()
	{
		if ( fixedTool is { IsLive: true } )
		{
			return fixedTool;
		}

		return RoomLayoutTool.LiveTool;
	}

	private static Layout AddRow( Widget parent )
	{
		var row = parent.Layout.AddRow( 0 );
		row.Spacing = 8;
		row.Alignment = TextFlag.LeftCenter;
		return row;
	}

	private SectionWidget AddSection( Widget parent, string title, int contentRows )
	{
		var section = new SectionWidget( parent );
		section.SetSizeMode( SizeMode.Flexible, SizeMode.CanShrink );
		section.Layout = Layout.Column();
		section.Layout.Alignment = TextFlag.LeftTop;
		section.Layout.Margin = 10;
		section.Layout.Spacing = 8;
		section.FixedHeight = SectionHeight( contentRows );
		parent.Layout.Add( section, 0 );

		var label = new Label( title, section );
		label.SetStyles( "font-weight: 600; color: #f2f2f2;" );
		section.Layout.Add( label, 0 );
		return section;
	}

	private static float SectionHeight( int contentRows )
	{
		var rowCount = Math.Max( 1, contentRows + 1 );
		var gaps = Math.Max( 0, rowCount - 1 );
		return SectionPaddingY + Theme.RowHeight * rowCount + SectionSpacing * gaps;
	}

	private Button AddButton( Layout row, string label, string icon, Action action, int fixedWidth = ActionButtonWidth )
	{
		var button = row.Add( new RoomLayoutButton( label, this ) );
		button.Icon = icon;
		button.FixedWidth = fixedWidth;
		button.FixedHeight = Theme.RowHeight;
		button.Clicked += action;
		return button;
	}

	private LineEdit AddCompactFloatEdit( Layout row, string labelText, string placeholder, Action action, int labelWidth = FieldLabelWidth )
	{
		var label = row.Add( new Label( labelText, this ) );
		label.FixedWidth = labelWidth;

		var edit = row.Add( new LineEdit( this ) );
		edit.FixedWidth = NumberEditWidth;
		edit.FixedHeight = Theme.RowHeight;
		edit.PlaceholderText = placeholder;
		edit.EditingFinished += action;
		edit.ReturnPressed += action;
		return edit;
	}

	private Checkbox AddCompactCheckbox( Layout row, string labelText, Action action, int labelWidth = FieldLabelWidth )
	{
		var label = row.Add( new Label( labelText, this ) );
		label.FixedWidth = labelWidth;

		var checkbox = row.Add( new Checkbox( this ) );
		checkbox.FixedWidth = CheckboxWidth;
		checkbox.Clicked += action;
		return checkbox;
	}

	private void AddSettingsTabsSection( Widget parent )
	{
		var tabRow = AddRow( parent );
		settingsTabs = tabRow.Add( new SegmentedControl( parent ) );
		settingsTabs.FixedWidth = 608;
		settingsTabs.AddOption( "Wall", "texture" );
		settingsTabs.AddOption( "Doors", "door_front" );
		settingsTabs.AddOption( "Windows", "window" );
		settingsTabs.AddOption( "Baseboards", "border_bottom" );
		settingsTabs.AddOption( "Thresholds", "door_front" );
		settingsTabs.AddOption( "Roof", "roofing" );
		settingsTabs.Selected = SettingsTabLabel( activeSettingsTab );
		settingsTabs.OnSelectedChanged = OnSettingsTabChanged;

		wallSettingsPanel = AddSettingsPanel( parent );
		var wallRow = AddRow( wallSettingsPanel );
		wallHeightEdit = AddCompactFloatEdit( wallRow, "Wall Height", "128", OnWallHeightEdited );

		doorSettingsPanel = AddSettingsPanel( parent );
		var doorSizeRow = AddRow( doorSettingsPanel );
		doorWidthEdit = AddCompactFloatEdit( doorSizeRow, "Default Door Width", "64", OnDoorWidthEdited, LongFieldLabelWidth );
		doorHeightEdit = AddCompactFloatEdit( doorSizeRow, "Default Door Height", "96", OnDoorHeightEdited, LongFieldLabelWidth );

		var doorFrameRow = AddRow( doorSettingsPanel );
		doorFrameThicknessEdit = AddCompactFloatEdit( doorFrameRow, "Door Frame Thickness", "8", OnDoorFrameThicknessEdited, LongFieldLabelWidth );

		windowSettingsPanel = AddSettingsPanel( parent );
		var windowSizeRow = AddRow( windowSettingsPanel );
		windowWidthEdit = AddCompactFloatEdit( windowSizeRow, "Default Window Width", "64", OnWindowWidthEdited, LongFieldLabelWidth );
		windowHeightEdit = AddCompactFloatEdit( windowSizeRow, "Default Window Height", "64", OnWindowHeightEdited, LongFieldLabelWidth );

		var windowDetailRow = AddRow( windowSettingsPanel );
		windowSillHeightEdit = AddCompactFloatEdit( windowDetailRow, "Default Sill Height", "48", OnWindowSillHeightEdited, LongFieldLabelWidth );
		windowFrameThicknessEdit = AddCompactFloatEdit( windowDetailRow, "Window Frame Thickness", "2", OnWindowFrameThicknessEdited, LongFieldLabelWidth );

		baseboardSettingsPanel = AddSettingsPanel( parent );
		var baseboardToggleRow = AddRow( baseboardSettingsPanel );
		baseboardsCheckbox = AddCompactCheckbox( baseboardToggleRow, "Baseboards", OnBaseboardsToggled );

		var baseboardSizeRow = AddRow( baseboardSettingsPanel );
		baseboardHeightEdit = AddCompactFloatEdit( baseboardSizeRow, "Baseboard Height", "4", OnBaseboardHeightEdited );
		baseboardDepthEdit = AddCompactFloatEdit( baseboardSizeRow, "Baseboard Depth", "2", OnBaseboardDepthEdited );

		thresholdSettingsPanel = AddSettingsPanel( parent );
		var thresholdToggleRow = AddRow( thresholdSettingsPanel );
		thresholdsCheckbox = AddCompactCheckbox( thresholdToggleRow, "Thresholds", OnThresholdsToggled );

		var thresholdSizeRow = AddRow( thresholdSettingsPanel );
		thresholdDepthEdit = AddCompactFloatEdit( thresholdSizeRow, "Threshold Depth", "12", OnThresholdDepthEdited );

		roofSettingsPanel = AddSettingsPanel( parent );
		var roofToggleRow = AddRow( roofSettingsPanel );
		roofCheckbox = AddCompactCheckbox( roofToggleRow, "Roof", OnRoofToggled );

		var roofSizeRow = AddRow( roofSettingsPanel );
		roofThicknessEdit = AddCompactFloatEdit( roofSizeRow, "Roof Thickness", "4", OnRoofThicknessEdited );

		UpdateSettingsTabVisibility();
	}

	private void AddDefaultMaterialsSection( Widget parent )
	{
		defaultMaterialsPanel = AddSection( parent, "Default Materials", contentRows: 10 );
		defaultMaterialsPanel.Visible = false;
		AddMaterialPicker( defaultMaterialsPanel, "Floor", RoomLayoutMaterialSlot.Floor, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Outer Wall", RoomLayoutMaterialSlot.OuterWall, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Inner Wall", RoomLayoutMaterialSlot.InnerWall, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Wall Cap", RoomLayoutMaterialSlot.WallCap, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Baseboard", RoomLayoutMaterialSlot.Baseboard, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Door Frame", RoomLayoutMaterialSlot.DoorFrame, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Window Frame", RoomLayoutMaterialSlot.WindowFrame, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Corridor Floor", RoomLayoutMaterialSlot.CorridorFloor, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Threshold", RoomLayoutMaterialSlot.Threshold, MaterialScope.Defaults );
		AddMaterialPicker( defaultMaterialsPanel, "Roof", RoomLayoutMaterialSlot.Roof, MaterialScope.Defaults );
	}

	private void OnSettingsTabChanged( string selected )
	{
		if ( TryParseSettingsTab( selected, out var tab ) )
		{
			SetSettingsTab( tab );
		}
	}

	private Widget AddSettingsPanel( Widget parent )
	{
		var panel = new Widget( parent );
		panel.Layout = Layout.Column();
		panel.Layout.Spacing = 8;
		parent.Layout.Add( panel, 0 );
		return panel;
	}

	private void SetSettingsTab( SettingsTab tab )
	{
		activeSettingsTab = tab;
		if ( settingsTabs is not null )
		{
			settingsTabs.Selected = SettingsTabLabel( tab );
		}

		UpdateSettingsTabVisibility();
	}

	private void UpdateSettingsTabVisibility()
	{
		if ( wallSettingsPanel is null )
		{
			return;
		}

		wallSettingsPanel.Visible = activeSettingsTab == SettingsTab.Wall;
		doorSettingsPanel.Visible = activeSettingsTab == SettingsTab.Doors;
		windowSettingsPanel.Visible = activeSettingsTab == SettingsTab.Windows;
		baseboardSettingsPanel.Visible = activeSettingsTab == SettingsTab.Baseboards;
		thresholdSettingsPanel.Visible = activeSettingsTab == SettingsTab.Thresholds;
		roofSettingsPanel.Visible = activeSettingsTab == SettingsTab.Roof;
	}

	private void AddSelectedSection( Widget parent )
	{
		var selectedRow = AddRow( parent );
		selectedLayout = selectedRow.Add( new Label( "Selected: none", parent ) );
		selectedLayout.FixedWidth = SelectedLabelWidth;
		selectLayoutButton = AddButton( selectedRow, "Select...", "ads_click", ShowLayoutSelectionMenu, SmallActionButtonWidth );
		deleteSelectedButton = AddButton( selectedRow, "Delete", "delete", OnDeleteSelectedClicked, SmallActionButtonWidth );
		ApplySubtleDangerStyle( deleteSelectedButton );
		deleteSelectedButton.ToolTip = "Delete the selected room, door, window, or corridor.";

		selectedDimensionsLabel = new Label( "Dimensions", parent );
		selectedDimensionsLabel.SetStyles( "font-weight: 600; color: #f2f2f2;" );
		parent.Layout.Add( selectedDimensionsLabel, 0 );

		selectedDoorDimensionsPanel = AddSettingsPanel( parent );
		var selectedDoorRow = AddRow( selectedDoorDimensionsPanel );
		selectedDoorWidthEdit = AddCompactFloatEdit( selectedDoorRow, "Door Width", "64", OnSelectedDoorWidthEdited );
		selectedDoorHeightEdit = AddCompactFloatEdit( selectedDoorRow, "Door Height", "96", OnSelectedDoorHeightEdited );

		selectedWindowDimensionsPanel = AddSettingsPanel( parent );
		var selectedWindowSizeRow = AddRow( selectedWindowDimensionsPanel );
		selectedWindowWidthEdit = AddCompactFloatEdit( selectedWindowSizeRow, "Window Width", "64", OnSelectedWindowWidthEdited );
		selectedWindowHeightEdit = AddCompactFloatEdit( selectedWindowSizeRow, "Window Height", "64", OnSelectedWindowHeightEdited );

		var selectedWindowSillRow = AddRow( selectedWindowDimensionsPanel );
		selectedWindowSillHeightEdit = AddCompactFloatEdit( selectedWindowSillRow, "Window Sill Height", "48", OnSelectedWindowSillHeightEdited, LongFieldLabelWidth );
		selectedDimensionsLabel.Visible = false;
		selectedDoorDimensionsPanel.Visible = false;
		selectedWindowDimensionsPanel.Visible = false;

		var materialLabel = new Label( "Material Overrides", parent );
		materialLabel.SetStyles( "font-weight: 600; color: #f2f2f2;" );
		parent.Layout.Add( materialLabel, 0 );

		AddMaterialPicker( parent, "Floor", RoomLayoutMaterialSlot.Floor, MaterialScope.Selected );
		AddMaterialPicker( parent, "Outer Wall", RoomLayoutMaterialSlot.OuterWall, MaterialScope.Selected );
		AddMaterialPicker( parent, "Inner Wall", RoomLayoutMaterialSlot.InnerWall, MaterialScope.Selected );
		AddMaterialPicker( parent, "Wall Cap", RoomLayoutMaterialSlot.WallCap, MaterialScope.Selected );
		AddMaterialPicker( parent, "Baseboard", RoomLayoutMaterialSlot.Baseboard, MaterialScope.Selected );
		AddMaterialPicker( parent, "Threshold", RoomLayoutMaterialSlot.Threshold, MaterialScope.Selected );
		AddMaterialPicker( parent, "Door Frame", RoomLayoutMaterialSlot.DoorFrame, MaterialScope.Selected );
		AddMaterialPicker( parent, "Window Frame", RoomLayoutMaterialSlot.WindowFrame, MaterialScope.Selected );
		AddMaterialPicker( parent, "Roof", RoomLayoutMaterialSlot.Roof, MaterialScope.Selected );
	}

	private void AddResetLayoutSection( Widget parent )
	{
		var resetRow = AddRow( parent );
		resetLayoutButton = AddButton( resetRow, "Reset Layout", "delete_forever", OnClearLayoutClicked, DangerButtonWidth );
		resetLayoutButton.ToolTip = "Delete the editable layout for the active scene.";
		ApplyDangerStyle( resetLayoutButton );
	}

	private void AddMaterialPicker( Widget parent, string label, RoomLayoutMaterialSlot slot, MaterialScope scope )
	{
		var row = AddFieldRow( parent, label );
		var key = new MaterialButtonKey( scope, slot );
		var button = row.Add( new RoomLayoutButton( "Pick", this ) );
		button.Icon = "texture";
		button.FixedWidth = MaterialButtonWidth;
		button.FixedHeight = Theme.RowHeight;
		button.Clicked += () => ShowMaterialMenu( key );
		materialButtons[key] = button;

		var scaleLabel = row.Add( new Label( "Scale", this ) );
		scaleLabel.FixedWidth = MaterialScaleLabelWidth;

		var scaleEdit = row.Add( new LineEdit( this ) );
		scaleEdit.FixedWidth = MaterialScaleEditWidth;
		scaleEdit.FixedHeight = Theme.RowHeight;
		scaleEdit.EditingFinished += () => OnMaterialScaleEdited( key );
		scaleEdit.ReturnPressed += () => OnMaterialScaleEdited( key );
		materialScaleEdits[key] = scaleEdit;
	}

	private Layout AddFieldRow( Widget parent, string labelText )
	{
		var row = parent.Layout.AddRow( 0 );
		row.Spacing = 8;
		row.Alignment = TextFlag.LeftCenter;

		var label = row.Add( new Label( labelText, this ) );
		label.FixedWidth = MaterialLabelWidth;
		return row;
	}

	private static void ApplySubtleDangerStyle( Button button )
	{
		button.Tint = Theme.Red.WithAlpha( 0.7f );
	}

	private static void ApplyDangerStyle( Button button )
	{
		button.Tint = Theme.Red;
	}

	private static Label AddStatusLabel( Widget parent )
	{
		var label = new Label( "", parent );
		parent.Layout.Add( label, 0 );
		return label;
	}

	private void ShowMaterialMenu( MaterialButtonKey key )
	{
		if ( GetTool() is null )
		{
			return;
		}

		var menu = new ContextMenu();
		menu.AddOption( "Pick Material", "texture", () => OpenAssetPicker( key, AssetType.Material ) );
		menu.AddSeparator();

		var clearText = key.Scope == MaterialScope.Selected ? "Inherit Default" : "Clear";
		menu.AddOption( clearText, "backspace", () => SetMaterialPath( key, "" ) );
		var scaleText = key.Scope == MaterialScope.Selected ? "Inherit Scale" : "Reset Scale";
		menu.AddOption( scaleText, "straighten", () => SetMaterialScale( key, 0.0f ) );
		menu.OpenAtCursor( false );
	}

	private void ToggleDefaultMaterialsPanel()
	{
		if ( defaultMaterialsPanel is null || GetTool() is null )
		{
			return;
		}

		defaultMaterialsPanel.Visible = !defaultMaterialsPanel.Visible;
	}

	private void ShowLayoutSelectionMenu()
	{
		var tool = GetTool();
		if ( tool is null )
		{
			return;
		}

		var menu = new ContextMenu();
		foreach ( var option in tool.GetSelectionOptions() )
		{
			var captured = option;
			menu.AddOption( captured.Name, captured.Icon, () => tool.SelectLayout( captured.Kind, captured.Id ) );
		}

		menu.OpenAtCursor( false );
	}

	private void OpenAssetPicker( MaterialButtonKey key, AssetType assetType )
	{
		var picker = AssetPicker.Create( this, assetType, new AssetPicker.PickerOptions
		{
			EnableCloud = true,
			EnableMultiselect = false
		} );
		picker.Title = $"Select {assetType.FriendlyName}";
		picker.SetSelection( CurrentMaterialPath( key ) );
		picker.OnAssetPicked = assets => SetMaterialFromAsset( key, assets.FirstOrDefault() );
		picker.Show();
	}

	private async void SetMaterialFromAsset( MaterialButtonKey key, Asset asset )
	{
		if ( asset is null )
		{
			return;
		}

		var path = asset.RelativePath;
		if ( asset.Package is { } package && AssetSystem.CanCloudInstall( package ) && !AssetSystem.IsCloudInstalled( package ) )
		{
			var installedAsset = await AssetSystem.InstallAsync( package.FullIdent );
			if ( installedAsset is not null )
			{
				asset = installedAsset;
				path = asset.RelativePath;
			}
		}

		path = NormalizeAssetPath( path );
		await WaitForMaterialAssetReady( asset, path );
		RoomLayoutGeometryBuilder.InvalidateMaterial( path );

		var tool = GetTool();
		var samePath = tool is not null &&
			string.Equals( MaterialPathForButton( tool, key ), path, StringComparison.OrdinalIgnoreCase );

		SetMaterialPath( key, path );
		if ( samePath )
		{
			tool.RefreshGeneratedGeometry();
		}
	}

	private static async Task WaitForMaterialAssetReady( Asset asset, string path )
	{
		if ( string.IsNullOrWhiteSpace( path ) )
		{
			return;
		}

		for ( var attempt = 0; attempt < 20; attempt++ )
		{
			if ( MaterialAssetReady( asset, path ) )
			{
				return;
			}

			await Task.Delay( 100 );
		}
	}

	private static bool MaterialAssetReady( Asset asset, string path )
	{
		try
		{
			var indexedAsset = asset ?? AssetSystem.FindByPath( path );
			if ( indexedAsset is not null && !indexedAsset.IsCompiledAndUpToDate )
			{
				return false;
			}

			if ( global::Editor.FileSystem.Content.FileExists( path ) ||
				Sandbox.FileSystem.Mounted.FileExists( path ) )
			{
				return true;
			}

			return indexedAsset?.Package is { } package
				? AssetSystem.IsCloudInstalled( package )
				: indexedAsset is not null;
		}
		catch
		{
			return false;
		}
	}

	private static string NormalizeAssetPath( string path )
	{
		if ( string.IsNullOrWhiteSpace( path ) )
		{
			return "";
		}

		path = path.Trim().Replace( '\\', '/' ).TrimStart( '/' );
		if ( path.StartsWith( "assets/", StringComparison.OrdinalIgnoreCase ) )
		{
			path = path[7..];
		}

		return path.EndsWith( ".vmat_c", StringComparison.OrdinalIgnoreCase )
			? path[..^2]
			: path;
	}

	private void SetMaterialPath( MaterialButtonKey key, string path )
	{
		var tool = GetTool();
		if ( tool is null )
		{
			return;
		}

		if ( key.Scope == MaterialScope.Defaults )
		{
			tool.SetMaterialPath( key.Slot, path );
			return;
		}

		tool.SetSelectedMaterialPath( key.Slot, path );
	}

	private void SetMaterialScale( MaterialButtonKey key, float scale )
	{
		var tool = GetTool();
		if ( tool is null )
		{
			return;
		}

		if ( key.Scope == MaterialScope.Defaults )
		{
			tool.SetMaterialScale( key.Slot, scale );
			return;
		}

		tool.SetSelectedMaterialScale( key.Slot, scale );
	}

	private string CurrentMaterialPath( MaterialButtonKey key )
	{
		var tool = GetTool();
		return tool is null ? "" : MaterialPathForButton( tool, key );
	}

	private static string MaterialPathForButton( RoomLayoutTool tool, MaterialButtonKey key )
	{
		return key.Scope == MaterialScope.Defaults
			? tool.GetMaterialPath( key.Slot )
			: tool.GetSelectedMaterialPath( key.Slot );
	}

	private static string MaterialButtonText( string path, bool allowInherit )
	{
		if ( string.IsNullOrWhiteSpace( path ) )
		{
			return allowInherit ? "Inherit" : "Built-in default";
		}

		var asset = AssetSystem.FindByPath( path );
		if ( asset is not null )
		{
			return asset.Name;
		}

		var normalized = path.Replace( '\\', '/' );
		var fileName = normalized[(normalized.LastIndexOf( '/' ) + 1)..];
		return System.IO.Path.GetFileNameWithoutExtension( fileName );
	}

	private static string MaterialButtonTooltip( string path, bool allowInherit )
	{
		if ( string.IsNullOrWhiteSpace( path ) )
		{
			return allowInherit
				? "Uses the default material for this surface."
				: "Uses the Interior Layout Builder built-in fallback material.";
		}

		return path;
	}

	private void OnRoomsClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.Rooms );
	}

	private void OnSelectClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.Select );
	}

	private void OnDoorsClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.Doors );
	}

	private void OnCorridorsClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.Corridors );
	}

	private void OnWindowsClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.Windows );
	}

	private void OnFloorCutoutsClicked()
	{
		GetTool()?.SetMode( RoomLayoutToolMode.FloorCutouts );
	}

	private void OnBuildClicked()
	{
		GetTool()?.RebuildGeneratedGeometry();
	}

	private void OnClearGeneratedClicked()
	{
		GetTool()?.ClearGeneratedGeometry();
	}

	private void OnDeleteSelectedClicked()
	{
		GetTool()?.DeleteSelectedLayout();
	}

	private void OnClearLayoutClicked()
	{
		GetTool()?.ClearLayout();
	}

	private void OnMaterialScaleEdited( MaterialButtonKey key )
	{
		if ( !materialScaleEdits.TryGetValue( key, out var edit ) )
		{
			return;
		}

		if ( string.IsNullOrWhiteSpace( edit.Value ) )
		{
			SetMaterialScale( key, 0.0f );
			return;
		}

		if ( TryReadFloatEdit( edit, out var scale ) )
		{
			SetMaterialScale( key, scale );
		}
	}

	private void OnWallHeightEdited()
	{
		if ( TryReadFloatEdit( wallHeightEdit, out var height ) )
		{
			GetTool()?.SetWallHeight( height );
		}
	}

	private void OnWindowWidthEdited()
	{
		if ( TryReadFloatEdit( windowWidthEdit, out var width ) )
		{
			GetTool()?.SetWindowWidth( width );
		}
	}

	private void OnDoorWidthEdited()
	{
		if ( TryReadFloatEdit( doorWidthEdit, out var width ) )
		{
			GetTool()?.SetDoorWidth( width );
		}
	}

	private void OnDoorHeightEdited()
	{
		if ( TryReadFloatEdit( doorHeightEdit, out var height ) )
		{
			GetTool()?.SetDoorHeight( height );
		}
	}

	private void OnDoorFrameThicknessEdited()
	{
		if ( TryReadFloatEdit( doorFrameThicknessEdit, out var thickness ) )
		{
			GetTool()?.SetDoorFrameThickness( thickness );
		}
	}

	private void OnSelectedDoorWidthEdited()
	{
		if ( TryReadFloatEdit( selectedDoorWidthEdit, out var width ) )
		{
			GetTool()?.SetSelectedDoorWidth( width );
		}
	}

	private void OnSelectedDoorHeightEdited()
	{
		if ( TryReadFloatEdit( selectedDoorHeightEdit, out var height ) )
		{
			GetTool()?.SetSelectedDoorHeight( height );
		}
	}

	private void OnSelectedWindowWidthEdited()
	{
		if ( TryReadFloatEdit( selectedWindowWidthEdit, out var width ) )
		{
			GetTool()?.SetSelectedWindowWidth( width );
		}
	}

	private void OnSelectedWindowHeightEdited()
	{
		if ( TryReadFloatEdit( selectedWindowHeightEdit, out var height ) )
		{
			GetTool()?.SetSelectedWindowHeight( height );
		}
	}

	private void OnSelectedWindowSillHeightEdited()
	{
		if ( TryReadFloatEdit( selectedWindowSillHeightEdit, out var height ) )
		{
			GetTool()?.SetSelectedWindowSillHeight( height );
		}
	}

	private void OnWindowHeightEdited()
	{
		if ( TryReadFloatEdit( windowHeightEdit, out var height ) )
		{
			GetTool()?.SetWindowHeight( height );
		}
	}

	private void OnWindowSillHeightEdited()
	{
		if ( TryReadFloatEdit( windowSillHeightEdit, out var height ) )
		{
			GetTool()?.SetWindowSillHeight( height );
		}
	}

	private void OnBaseboardsToggled()
	{
		GetTool()?.SetBaseboardsEnabled( baseboardsCheckbox.State == CheckState.On );
	}

	private void OnThresholdsToggled()
	{
		GetTool()?.SetThresholdsEnabled( thresholdsCheckbox.State == CheckState.On );
	}

	private void OnRoofToggled()
	{
		GetTool()?.SetRoofEnabled( roofCheckbox.State == CheckState.On );
	}

	private void OnBaseboardHeightEdited()
	{
		if ( TryReadFloatEdit( baseboardHeightEdit, out var height ) )
		{
			GetTool()?.SetBaseboardHeight( height );
		}
	}

	private void OnBaseboardDepthEdited()
	{
		if ( TryReadFloatEdit( baseboardDepthEdit, out var depth ) )
		{
			GetTool()?.SetBaseboardDepth( depth );
		}
	}

	private void OnThresholdDepthEdited()
	{
		if ( TryReadFloatEdit( thresholdDepthEdit, out var depth ) )
		{
			GetTool()?.SetThresholdDepth( depth );
		}
	}

	private void OnRoofThicknessEdited()
	{
		if ( TryReadFloatEdit( roofThicknessEdit, out var thickness ) )
		{
			GetTool()?.SetRoofThickness( thickness );
		}
	}

	private static bool TryReadFloatEdit( LineEdit edit, out float value )
	{
		value = 0.0f;
		var text = edit?.Value?.Trim();
		return !string.IsNullOrEmpty( text ) &&
			(float.TryParse( text, NumberStyles.Float, CultureInfo.InvariantCulture, out value ) ||
				float.TryParse( text, NumberStyles.Float, CultureInfo.CurrentCulture, out value ));
	}

	private enum MaterialScope
	{
		Defaults,
		Selected
	}

	private enum SettingsTab
	{
		Wall,
		Doors,
		Windows,
		Baseboards,
		Thresholds,
		Roof
	}

	private static string SettingsTabLabel( SettingsTab tab )
	{
		return tab switch
		{
			SettingsTab.Wall => "Wall",
			SettingsTab.Doors => "Doors",
			SettingsTab.Windows => "Windows",
			SettingsTab.Baseboards => "Baseboards",
			SettingsTab.Thresholds => "Thresholds",
			SettingsTab.Roof => "Roof",
			_ => "Wall"
		};
	}

	private static bool TryParseSettingsTab( string label, out SettingsTab tab )
	{
		tab = label switch
		{
			"Wall" => SettingsTab.Wall,
			"Doors" => SettingsTab.Doors,
			"Windows" => SettingsTab.Windows,
			"Baseboards" => SettingsTab.Baseboards,
			"Thresholds" => SettingsTab.Thresholds,
			"Roof" => SettingsTab.Roof,
			_ => SettingsTab.Wall
		};

		return label is "Wall" or "Doors" or "Windows" or "Baseboards" or "Thresholds" or "Roof";
	}

	private sealed class RoomLayoutButton : Button
	{
		public RoomLayoutButton( string title, Widget parent ) : base( title, parent )
		{
		}

		protected override void OnPaint()
		{
			var color = Tint.ToHsv();
			var background = color;

			if ( Enabled )
			{
				if ( Paint.HasPressed )
				{
					background = color with { Value = color.Value + 0.1f };
				}
				else if ( Paint.HasMouseOver )
				{
					background = color with { Value = color.Value + 0.2f };
				}
			}
			else
			{
				background = color = Theme.SurfaceLightBackground;
			}

			if ( !Enabled || ReadOnly )
			{
				color = color.WithSaturation( 0.1f ).WithAlpha( 0.5f );
				background = color.WithAlpha( 0.2f );
			}

			if ( background.Alpha > 0 )
			{
				Paint.Antialiasing = true;
				Paint.ClearPen();
				Paint.SetBrush( background with { Value = background.Value + 0.04f, Saturation = color.Saturation * 0.8f } );
				Paint.DrawRect( LocalRect, 3 );

				Paint.SetBrushLinear( LocalRect.TopLeft, LocalRect.BottomRight, background, background with { Value = background.Value - 0.03f } );
				Paint.DrawRect( LocalRect.Shrink( 1, 1, 1, 1 ), 3 );
			}
			else
			{
				color = Color.White.WithAlpha( 0.5f );
			}

			Paint.SetDefaultFont();
			Paint.SetPen( color with { Value = 0.99f, Saturation = color.Saturation * 0.20f } );

			var rect = LocalRect.Shrink( 8, 0, 8, 0 );
			if ( !string.IsNullOrWhiteSpace( Icon ) )
			{
				var iconRect = new Rect( rect.Left, rect.Center.y - 8, 16, 16 );
				Paint.DrawIcon( iconRect, Icon, 16 );
				rect.Left += 24;
			}

			Paint.DrawText( rect, Text, TextFlag.LeftCenter );
		}
	}

	private sealed class SectionWidget : Widget
	{
		public SectionWidget( Widget parent ) : base( parent )
		{
		}

		protected override void OnPaint()
		{
			var rect = LocalRect;
			var fill = new Color( 0.155f, 0.155f, 0.155f, 1.0f );
			var border = new Color( 0.255f, 0.255f, 0.255f, 1.0f );
			Paint.Antialiasing = true;
			Paint.ClearPen();
			Paint.SetBrush( fill );
			Paint.DrawRect( rect, 3 );

			Paint.SetBrush( border );
			Paint.DrawRect( new Rect( rect.Left, rect.Top, rect.Width, 1 ), 0 );
			Paint.DrawRect( new Rect( rect.Left, rect.Bottom - 1, rect.Width, 1 ), 0 );
			Paint.DrawRect( new Rect( rect.Left, rect.Top, 1, rect.Height ), 0 );
			Paint.DrawRect( new Rect( rect.Right - 1, rect.Top, 1, rect.Height ), 0 );
		}
	}

	private readonly record struct MaterialButtonKey( MaterialScope Scope, RoomLayoutMaterialSlot Slot );
}