Editor/Output/ArchHost.cs
namespace Sunless.Architecture;

// What the tool cannot know about the game it generates into. A game answers here rather than repainting what the
// tool produced; nothing answering is the tool standing on its own, so every fact has a stock answer.
public interface IArchHost {
	// The shader a generated blockout material is written against. A game with its own lighting model names it
	// here instead of rewriting every .vmat the tool just wrote.
	string BlockoutShader { get; }

	// Where the material browser looks for the surface library, relative to the open project's root.
	string MaterialLibrary { get; }

	// Folder under Assets/ every catalogue is read from and written to. A game that files data beside the asset
	// it belongs to names that folder here; the author can still override it in the tool's settings.
	string DataRoot { get; }

	// Where generated blockout materials are written. The grid texture they sample still ships with the library.
	string BlockoutFolder { get; }
}

// Never held between asks, for the reason ArchDoorFitters is not: hotload carries a static over.
public sealed class ArchHost {
	// The library's own, because complex.shader tints through g_vColorTint and every generated blockout material
	// states its colour as g_flTintColor - on the stock shader the tint was dropped and the lot rendered white.
	public const string StockShader = "shaders/arch_blockout.shader";

	public const string StockLibrary = "Assets/materials";

	public const string StockDataRoot = "arch";

	public const string StockBlockoutFolder = "materials/blockout";

	public string BlockoutShader { get; private init; } = StockShader;

	public string MaterialLibrary { get; private init; } = StockLibrary;

	public string DataRoot { get; private init; } = StockDataRoot;

	public string BlockoutFolder { get; private init; } = StockBlockoutFolder;

	public bool IsStock => BlockoutShader == StockShader && MaterialLibrary == StockLibrary
		&& DataRoot == StockDataRoot && BlockoutFolder == StockBlockoutFolder;

	// Ordered by name and first answer taken, so two games open in one editor cannot make the result depend on
	// which assembly enrolled first. A fact left blank falls back rather than writing an empty shader path.
	public static ArchHost Load() {
		var spoken = ArchDiscovery.EnrolledByName<IArchHost>().FirstOrDefault();

		if ( spoken is null ) {
			return new ArchHost();
		}

		return new ArchHost {
			BlockoutShader = Answered( spoken.BlockoutShader, StockShader ),
			MaterialLibrary = Answered( spoken.MaterialLibrary, StockLibrary ),
			DataRoot = Answered( spoken.DataRoot, StockDataRoot ),
			BlockoutFolder = Answered( spoken.BlockoutFolder, StockBlockoutFolder )
		};
	}

	static string Answered( string answer, string stock ) {
		return string.IsNullOrWhiteSpace( answer ) ? stock : answer;
	}
}