Editor/Floor/ArchBasementWell.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

public sealed class ArchBasementWellShape
{
	public List<Vector2> Outline { get; init; }
	public Vector2 WallLeft { get; init; }
	public Vector2 WallRight { get; init; }
	public Vector2 OuterLeft { get; init; }
	public Vector2 OuterRight { get; init; }
	public float Bottom { get; init; }
	public float Top { get; init; }
	public float Thickness { get; init; }
}

public static class ArchBasementWell
{
	public static ArchBasementWellShape Resolve( ArchRoom room, ArchWall wall, ArchOpening opening, ArchBuilding building, ArchKit kit )
	{
		if ( room is null || wall is null || opening is null || building is null || kit is null ||
			room.Floor >= 0 || !opening.BasementWell || opening.Kind != OpeningKind.Window )
		{
			return null;
		}

		var outward = ArchWallFaces.Outward( wall, room, building );
		var along = wall.Direction;
		var centre = wall.PointAt( opening.Offset );
		var halfWidth = opening.Width * 0.5f + MathF.Max( 4f, kit.WallThickness * 0.5f );
		var wallFace = (wall.Thickness > 0f ? wall.Thickness : kit.WallThickness) * 0.5f;
		var depth = MathF.Max( 18f, opening.BasementWellDepth );
		var thickness = MathF.Max( 3f, MathF.Min( 6f, kit.WallThickness * 0.5f ) );
		var wallLeft = centre - along * halfWidth + outward * wallFace;
		var wallRight = centre + along * halfWidth + outward * wallFace;
		var outerLeft = wallLeft + outward * depth;
		var outerRight = wallRight + outward * depth;
		var sill = room.BaseHeight + opening.SillHeight;

		return new ArchBasementWellShape
		{
			Outline = new List<Vector2> { wallLeft, outerLeft, outerRight, wallRight },
			WallLeft = wallLeft,
			WallRight = wallRight,
			OuterLeft = outerLeft,
			OuterRight = outerRight,
			Bottom = sill - MathF.Max( 6f, kit.FloorThickness ),
			Top = ArchBuild.FloorOf( building, kit, 0 ) + MathF.Max( 4f, thickness ),
			Thickness = thickness
		};
	}

	public static void Build( ArchMesh canvas, ArchBasementWellShape shape, ArchBrush brush )
	{
		if ( canvas is null || shape is null || shape.Top - shape.Bottom < 1f )
		{
			return;
		}

		var floorBottom = shape.Bottom - MathF.Max( 3f, shape.Thickness );

		Solid( canvas, shape.Outline, floorBottom, shape.Bottom, brush );
		Wall( canvas, shape.OuterLeft, shape.OuterRight, shape.Thickness, shape.Bottom, shape.Top, brush );
		Wall( canvas, shape.WallLeft, shape.OuterLeft, shape.Thickness, shape.Bottom, shape.Top, brush );
		Wall( canvas, shape.OuterRight, shape.WallRight, shape.Thickness, shape.Bottom, shape.Top, brush );
	}

	static void Wall( ArchMesh canvas, Vector2 from, Vector2 to, float thickness, float bottom, float top, ArchBrush brush )
	{
		var span = to - from;

		if ( span.IsNearZeroLength )
		{
			return;
		}

		var half = new Vector2( -span.Normal.y, span.Normal.x ) * thickness * 0.5f;

		Solid( canvas, new List<Vector2> { from - half, to - half, to + half, from + half }, bottom, top, brush );
	}

	static void Solid( ArchMesh canvas, IReadOnlyList<Vector2> outline, float bottom, float top, ArchBrush brush )
	{
		var lower = outline.Select( point => new Vector3( point.x, point.y, bottom ) ).ToList();
		var upper = outline.Select( point => new Vector3( point.x, point.y, top ) ).ToList();

		canvas.Prism( lower, upper, brush );
	}
}