Editor/Roof/ArchRoofGuard.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

// The barrier engine already owns posts, bays and rails; a roof only says where its edge is and how high
// to stand on it. Nothing about a guardrail is re-implemented here.
public static partial class ArchRoofGen {
	public static List<ArchGuardRun> Guardrails( ArchRoofPart roof, ArchBuilding building, ArchKit kit ) {
		var guards = new List<ArchGuardRun>();

		if ( !roof.Guardrail || building is null ) {
			return guards;
		}

		var abutments = Abutments( roof, building, ArchKinds.Load() );

		bool Abuts( Vector2 point, Vector2 outward ) => Buried( abutments, point + outward * ArchProbe.Step );

		foreach ( var region in new ArchRoofRegionService( building, roof ).Resolve() ) {
			guards.AddRange( Guardrails( roof, kit, region.Outer, Abuts ) );
		}

		return guards;
	}

	static List<ArchGuardRun> Guardrails( ArchRoofPart roof, ArchKit kit, IReadOnlyList<Vector2> outline, Func<Vector2, Vector2, bool> abuts ) {
		var guards = new List<ArchGuardRun>();

		foreach ( var run in Runs( ArchFootprint.Wind( outline.ToList() ), GuardSeat( roof, kit ), abuts ) ) {
			var spec = GuardSpec( roof, kit, run );
			var curve = ArchCurve.Polyline( run.Raised(), run.Closed );

			guards.Add( new ArchGuardRun {
				Shape = ArchBarrierShape.Resolve( curve, spec, 0f, 0f, kit ),
				Spec = spec,
				Closed = run.Closed,
				Length = curve.Length
			} );
		}

		return guards;
	}

	static void Guardrail( ArchMesh canvas, ArchRoofPart roof, IReadOnlyList<Vector2> outline, ArchKit kit, ArchStyle style, ArchPalette[] chain, Func<Vector2, Vector2, bool> abuts ) {
		var brushes = ArchBarrierBrushes.Of( style, chain );

		foreach ( var guard in Guardrails( roof, kit, outline, abuts ) ) {
			ArchBarrierGen.Build( canvas, guard.Shape, guard.Spec, kit, brushes, guard.Doubled() );
		}
	}

	// It stands on whatever finishes the edge: the parapet's top where there is one, the deck where there is not.
	public static float GuardSeat( ArchRoofPart roof, ArchKit kit ) {
		if ( roof.Parapet ) {
			return roof.BaseHeight + MathF.Max( 4f, roof.ParapetHeight );
		}

		return roof.BaseHeight + ArchRoofPlane.Thickness( roof, kit );
	}

	static ArchBarrierSpec GuardSpec( ArchRoofPart roof, ArchKit kit, ArchRunPath run ) {
		return ArchGuards.Spec( MathF.Max( 12f, roof.GuardHeight ), kit, run, roof.GuardStyle );
	}
}