Editor/Layers/ArchAffectors.cs
namespace Sunless.Architecture;

public interface IArchAffector {
	ArchKind Kind { get; }

	int Order { get; }

	int Resolve( ArchPlan plan, ArchKit kit );

	bool Closes( object payload );

	void Close( ArchPlan plan, object payload );
}

public sealed class ArchAffectors : ArchTable<ArchAffectors, ArchKind, IArchAffector> {
	protected override IEnumerable<IArchAffector> Standing() {
		yield return new ArchCutEffects();
		yield return new ArchTrimEffects();
		yield return new ArchOpeningRunEffects();
		yield return new ArchBalconyEffects();
		yield return new ArchStairEffects();
		yield return new ArchExteriorStairEffects();
		yield return new ArchPorchEffects();
		yield return new ArchApproachEffects();
		yield return new ArchWalkwayAffector();
	}

	protected override ArchKind KeyOf( IArchAffector affector ) => affector.Kind;

	protected override bool Accepts( IArchAffector affector ) => affector.Kind.Known;

	protected override string Collision( IArchAffector standing, IArchAffector affector ) {
		return $"{affector.Kind.Ident} affector is claimed by both {standing.GetType().Name} and {affector.GetType().Name}";
	}

	public IArchAffector For( ArchKind kind ) => Held( kind );

	public IReadOnlyList<IArchAffector> Ordered => Entries
		.OrderBy( affector => affector.Order )
		.ThenBy( affector => affector.Kind.Ident, StringComparer.Ordinal )
		.ToList();

	public int Run( ArchPlan plan, ArchKit kit ) {
		var resolved = Ordered.Sum( affector => affector.Resolve( plan, kit ) );

		Tidy( plan );

		return resolved;
	}

	public void CloseMatching( ArchPlan plan, object payload ) {
		if ( plan is null || payload is null ) {
			return;
		}

		foreach ( var affector in Ordered.Where( candidate => candidate.Closes( payload ) ) ) {
			affector.Close( plan, payload );
		}
	}

	public static int Resolve( ArchPlan plan, ArchKit kit ) => Load().Run( plan, kit );

	public static void Close( ArchPlan plan, object payload ) => Load().CloseMatching( plan, payload );

	static void Tidy( ArchPlan plan ) {
		foreach ( var empty in plan.Assemblies.Where( group => group.Children.Count == 0 ).ToList() ) {
			ArchLayerGroups.Dissolve( plan, empty );
		}
	}
}