Editor/Tool/ArchFixtureRoles.cs
using System.Collections.Generic;

namespace Sunless.Architecture;

// What the fixture tool authors that it does not own. Each role previews its own draft and places it, because the
// shape a preview draws belongs to the role and the fixture tool has no business resolving one. An unanswered role
// is a fixture that simply cannot be stood, rather than an error.
public interface IArchLadders {
	bool Preview( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchLadderPart draft );

	ArchLadderPart Place( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchLadderPart draft, out ArchBuilding host );

	// Handed the buildings, because a recipe hangs a ladder on a building not yet filed in the plan.
	ArchLadderPart Place( IEnumerable<ArchBuilding> buildings, ArchPlan plan, ArchKit kit, Vector2 point, ArchLadderPart draft, out ArchBuilding host );
}

public interface IArchBalconies {
	bool Preview( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchBalconyPart draft );

	ArchBalconyPart Place( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchBalconyPart draft, out ArchBuilding host );

	void Remove( ArchPlan plan, ArchBuilding building, ArchBalconyPart balcony );
}

// What a recess or a proud pier takes out of a wall, drawn by the module that cuts it - a block is a stack of slabs
// on either face, not a box, so the role draws its own ghost rather than handing back an envelope.
public interface IArchWallMods {
	bool Preview( ArchWallModPart part, ArchWall wall, ArchKit kit, float wallHeight, float floor );
}

public static class ArchLadders {
	static IArchLadders Load() => ArchRoles.One<IArchLadders>();

	public static bool Preview( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchLadderPart draft ) {
		return Load()?.Preview( plan, kit, at, draft ) == true;
	}

	public static ArchLadderPart Place( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchLadderPart draft, out ArchBuilding host ) {
		if ( Load() is not { } ladders ) {
			host = null;

			return null;
		}

		return ladders.Place( plan, kit, at, draft, out host );
	}

	public static ArchLadderPart Place( IEnumerable<ArchBuilding> buildings, ArchPlan plan, ArchKit kit, Vector2 point, ArchLadderPart draft, out ArchBuilding host ) {
		if ( Load() is not { } ladders ) {
			host = null;

			return null;
		}

		return ladders.Place( buildings, plan, kit, point, draft, out host );
	}
}

public static class ArchBalconies {
	static IArchBalconies Load() => ArchRoles.One<IArchBalconies>();

	public static bool Preview( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchBalconyPart draft ) {
		return Load()?.Preview( plan, kit, at, draft ) == true;
	}

	public static ArchBalconyPart Place( ArchPlan plan, ArchKit kit, ArchFixtureStation? at, ArchBalconyPart draft, out ArchBuilding host ) {
		if ( Load() is not { } balconies ) {
			host = null;

			return null;
		}

		return balconies.Place( plan, kit, at, draft, out host );
	}

	public static void Remove( ArchPlan plan, ArchBuilding building, ArchBalconyPart balcony ) {
		Load()?.Remove( plan, building, balcony );
	}
}

public static class ArchWallModBlocks {
	public static bool Preview( ArchWallModPart part, ArchWall wall, ArchKit kit, float wallHeight, float floor ) {
		return ArchRoles.One<IArchWallMods>() is { } mods && mods.Preview( part, wall, kit, wallHeight, floor );
	}
}

// The single-implementation half of ArchAnswers, for roles a caller reaches with no table of its own. A role
// nothing answers hands back null, which every caller falls back from rather than erroring.
public static class ArchRoles {
	public static T One<T>() where T : class => Answering( typeof( T ) ) as T;

	static object Answering( Type role ) {
		if ( role == typeof( IArchWallForms ) ) return new ArchWallFormsRole();
		if ( role == typeof( IArchWallMods ) ) return new ArchWallModRole();
		if ( role == typeof( IArchBalconies ) ) return new ArchBalconyRole();
		if ( role == typeof( IArchLadders ) ) return new ArchLadderRole();
		if ( role == typeof( IArchFlightBuilder ) ) return new ArchFlightBuilder();
		if ( role == typeof( IArchWellEdges ) ) return new ArchStairWellEdges();
		if ( role == typeof( IArchPiercer ) ) return new ArchStairPiercer();
		if ( role == typeof( IArchHostCarving ) ) return new ArchStairHostCarving();
		if ( role == typeof( IArchExteriorStairs ) ) return new ArchExteriorStairAuthoring();

		return null;
	}
}