Editor/Output/ArchBuiltPieces.cs
using System.Collections.Generic;
using Sandbox;

namespace Sunless.Architecture;

// One named piece as it actually came out - the object it became, whether it renders anything of its own, and
// the pieces nested inside it.
public sealed class ArchBuiltPiece {
	public string Name { get; init; }
	// The path under its layer: "Gutters", "Gutters/Fascia".
	public string Piece { get; init; }
	public int Owner { get; init; }
	public GameObject Object { get; init; }
	public bool Renders { get; init; }
	public List<ArchBuiltPiece> Children { get; } = new();

	public string Key => ArchParts.Key( Owner, Piece );
}

// WHAT EACH LAYER BUILT, as a tree - read off the scene rather than declared per kind, because a table of
// "a roof has a fascia" is a second opinion the generators are free to drift from. A generated name carrying
// a plan id starts a new layer; anything else is a named piece of the layer above it.
public static class ArchBuiltPieces {
	public static Dictionary<int, List<ArchBuiltPiece>> Index( Scene scene ) {
		var index = new Dictionary<int, List<ArchBuiltPiece>>();

		foreach ( var root in ArchScene.FindRoots( scene ) ) {
			foreach ( var child in root.Children ) {
				Walk( index, child, 0, "" );
			}
		}

		return index;
	}

	static ArchBuiltPiece Walk( Dictionary<int, List<ArchBuiltPiece>> index, GameObject node, int owner, string under ) {
		if ( ArchNames.TrySourceId( node.Name, out var id ) ) {
			foreach ( var child in node.Children ) {
				if ( Walk( index, child, id, "" ) is { } piece ) {
					Filed( index, id ).Add( piece );
				}
			}

			return null;
		}

		var named = ArchParts.Joined( under, node.Name );

		var built = new ArchBuiltPiece {
			Name = node.Name,
			Piece = named,
			Owner = owner,
			Object = node,
			Renders = node.Components.Get<MeshComponent>( FindMode.EverythingInSelf ) is { Mesh: not null }
		};

		foreach ( var child in node.Children ) {
			if ( Walk( index, child, owner, named ) is { } nested ) {
				built.Children.Add( nested );
			}
		}

		// Nothing generated stands outside a building or a road, so a piece with no layer over it is a node
		// somebody added by hand and none of the stack's business.
		return owner == 0 ? null : built;
	}

	static List<ArchBuiltPiece> Filed( Dictionary<int, List<ArchBuiltPiece>> index, int owner ) {
		if ( index.TryGetValue( owner, out var held ) ) {
			return held;
		}

		return index[owner] = new List<ArchBuiltPiece>();
	}
}