Editor/InteriorLayoutBuilder/RoomLayoutGeometryBuilder.Corners.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace ReusableRoomLayout;

public sealed partial class RoomLayoutGeometryBuilder
{
	private static void BuildRoomCorners(
		ICollection<RoomLayoutSolidSlab> solidSlabs,
		RoomLayoutDocument document,
		RoomLayoutRoom room )
	{
		var bounds = room.Bounds;
		var settings = document.Settings;
		var thickness = settings.WallThickness;
		var baseboardInset = settings.BaseboardDepth * 0.5f - TrimEmbedForDepth( settings.BaseboardDepth );

		BuildRoomCorner(
			solidSlabs,
			document,
			room,
			"NW",
			RoomLayoutWallSide.North,
			0.0f,
			RoomLayoutWallSide.West,
			bounds.Height,
			new Vector2( bounds.X - thickness * 0.5f, bounds.Y + bounds.Height + thickness * 0.5f ),
			new Vector2( bounds.X + baseboardInset, bounds.Y + bounds.Height - baseboardInset ) );

		BuildRoomCorner(
			solidSlabs,
			document,
			room,
			"NE",
			RoomLayoutWallSide.North,
			bounds.Width,
			RoomLayoutWallSide.East,
			bounds.Height,
			new Vector2( bounds.X + bounds.Width + thickness * 0.5f, bounds.Y + bounds.Height + thickness * 0.5f ),
			new Vector2( bounds.X + bounds.Width - baseboardInset, bounds.Y + bounds.Height - baseboardInset ) );

		BuildRoomCorner(
			solidSlabs,
			document,
			room,
			"SE",
			RoomLayoutWallSide.South,
			bounds.Width,
			RoomLayoutWallSide.East,
			0.0f,
			new Vector2( bounds.X + bounds.Width + thickness * 0.5f, bounds.Y - thickness * 0.5f ),
			new Vector2( bounds.X + bounds.Width - baseboardInset, bounds.Y + baseboardInset ) );

		BuildRoomCorner(
			solidSlabs,
			document,
			room,
			"SW",
			RoomLayoutWallSide.South,
			0.0f,
			RoomLayoutWallSide.West,
			0.0f,
			new Vector2( bounds.X - thickness * 0.5f, bounds.Y - thickness * 0.5f ),
			new Vector2( bounds.X + baseboardInset, bounds.Y + baseboardInset ) );
	}

	private static void BuildRoomCorner(
		ICollection<RoomLayoutSolidSlab> solidSlabs,
		RoomLayoutDocument document,
		RoomLayoutRoom room,
		string suffix,
		RoomLayoutWallSide firstSide,
		float firstOffset,
		RoomLayoutWallSide secondSide,
		float secondOffset,
		Vector2 center,
		Vector2 baseboardCenter )
	{
		var settings = document.Settings;
		var hasFirstWall = HasSolidWallAt( document, room, firstSide, firstOffset );
		var hasSecondWall = HasSolidWallAt( document, room, secondSide, secondOffset );
		if ( !hasFirstWall && !hasSecondWall )
		{
			return;
		}

		var footprint = new Vector3( settings.WallThickness, settings.WallThickness, settings.WallHeight );
		var cap = new Vector3( settings.WallThickness, settings.WallThickness, settings.WallCapHeight );
		var name = $"{RoomName( room )} Corner {suffix}";
		var wallCenter = new Vector3( center.x, center.y, settings.WallHeight * 0.5f );
		var capCenter = new Vector3( center.x, center.y, settings.WallHeight + settings.WallCapHeight * 0.5f );
		var wallMaterialPath = RoomMaterialPath( settings, room, RoomLayoutSurface.Wall );
		var innerWallMaterialPath = RoomInnerWallMaterialPath( settings, room );
		var capMaterialPath = RoomMaterialPath( settings, room, RoomLayoutSurface.Cap );
		var wallMaterialScale = RoomMaterialScale( settings, room, RoomLayoutSurface.Wall );
		var innerWallMaterialScale = RoomInnerWallMaterialScale( settings, room );
		var capMaterialScale = RoomMaterialScale( settings, room, RoomLayoutSurface.Cap );

		var northMaterialPath = wallMaterialPath;
		var southMaterialPath = wallMaterialPath;
		var eastMaterialPath = wallMaterialPath;
		var westMaterialPath = wallMaterialPath;
		var northMaterialScale = wallMaterialScale;
		var southMaterialScale = wallMaterialScale;
		var eastMaterialScale = wallMaterialScale;
		var westMaterialScale = wallMaterialScale;
		var bounds = room.Bounds;

		if ( center.x < bounds.X )
		{
			eastMaterialPath = innerWallMaterialPath;
			eastMaterialScale = innerWallMaterialScale;
		}
		else if ( center.x > bounds.X + bounds.Width )
		{
			westMaterialPath = innerWallMaterialPath;
			westMaterialScale = innerWallMaterialScale;
		}

		if ( center.y < bounds.Y )
		{
			northMaterialPath = innerWallMaterialPath;
			northMaterialScale = innerWallMaterialScale;
		}
		else if ( center.y > bounds.Y + bounds.Height )
		{
			southMaterialPath = innerWallMaterialPath;
			southMaterialScale = innerWallMaterialScale;
		}

		AddSolidSlab(
			solidSlabs,
			name,
			wallCenter,
			footprint,
			RoomLayoutSurface.Wall,
			materialPath: wallMaterialPath,
			textureWorldSize: wallMaterialScale,
			northMaterialPath: northMaterialPath,
			southMaterialPath: southMaterialPath,
			eastMaterialPath: eastMaterialPath,
			westMaterialPath: westMaterialPath,
			northTextureWorldSize: northMaterialScale,
			southTextureWorldSize: southMaterialScale,
			eastTextureWorldSize: eastMaterialScale,
			westTextureWorldSize: westMaterialScale );
		AddSolidSlab( solidSlabs, $"{name} Cap", capCenter, cap, RoomLayoutSurface.Cap, materialPath: capMaterialPath, textureWorldSize: capMaterialScale );
		if ( hasFirstWall && hasSecondWall )
		{
			BuildBaseboardCorner(
				solidSlabs,
				settings,
				$"{name} Baseboard",
				baseboardCenter,
				RoomMaterialPath( settings, room, RoomLayoutSurface.Baseboard ),
				RoomMaterialScale( settings, room, RoomLayoutSurface.Baseboard ) );
		}
	}

	private static void BuildBaseboardCorner(
		ICollection<RoomLayoutSolidSlab> solidSlabs,
		RoomLayoutSettings settings,
		string name,
		Vector2 center,
		string materialPath,
		float textureWorldSize )
	{
		if ( !settings.BaseboardsEnabled ||
			settings.BaseboardHeight < 0.5f ||
			settings.BaseboardDepth < 0.5f )
		{
			return;
		}

		AddSolidSlab(
			solidSlabs,
			name,
			new Vector3( center.x, center.y, settings.BaseboardHeight * 0.5f ),
			new Vector3( settings.BaseboardDepth, settings.BaseboardDepth, settings.BaseboardHeight ),
			RoomLayoutSurface.Baseboard,
			hasCollider: false,
			materialPath: materialPath,
			textureWorldSize: textureWorldSize );
	}

	private static bool HasSolidWallAt( RoomLayoutDocument document, RoomLayoutRoom room, RoomLayoutWallSide side, float offset )
	{
		var length = side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
			? room.Bounds.Width
			: room.Bounds.Height;
		var ownDoors = document.Doors
			.Where( door => door.RoomId == room.Id && door.Side == side )
			.ToArray();
		var openings = WallOpeningsForSide( document, room, side, ownDoors );
		var minimumWallRun = MinimumWallRunLength( document.Settings );

		foreach ( var opening in openings )
		{
			var openStart = Math.Clamp( opening.Start, 0.0f, length );
			var openEnd = Math.Clamp( opening.End, 0.0f, length );
			SnapOpeningToWallEnds( length, minimumWallRun, ref openStart, ref openEnd );
			if ( offset >= openStart - 0.5f && offset <= openEnd + 0.5f )
			{
				return false;
			}
		}

		return true;
	}
}