using System.Collections.Generic;
using System.Linq;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.Progression;
namespace Mr.BrickAdventures.scripts.Resources;
///
/// The authored list of Levels in campaign order, and the bridge from a back to
/// the Godot scene that implements it.
///
///
/// Not a domain port. Nothing in the progression domain resolves a Level to a scene — the domain only
/// needs the ordering, which it takes as a . Scene lookup is wanted solely by
/// the composition root, which is engine-side anyway.
///
[GlobalClass]
public partial class LevelCatalogue : Resource
{
///
/// Levels in campaign order. A Level absent from this list is not reachable, even if its
/// exists.
///
[Export]
public Array Levels { get; set; } = [];
///
/// The ordering the domain works from. Throws if the authored data is inconsistent — a blank or
/// duplicated id cannot produce a , so a mistake fails at boot rather than
/// corrupting a save later.
///
public Campaign ToCampaign()
{
var ids = new List();
for (var i = 0; i < Levels.Count; i++)
{
if (Levels[i] == null)
throw new System.InvalidOperationException($"LevelCatalogue has an empty slot at index {i}.");
ids.Add(LevelId.Of(Levels[i].Id));
}
return Campaign.Of(ids);
}
///
/// Checks every catalogued Level names a scene that actually exists, returning the problems found.
///
///
/// This is what keeps honest. The path cannot be a typed
/// PackedScene — several level scenes reference their own LevelResource, so a reference back
/// to the scene is a cycle Godot reports as "Parse Error: Busy" and the level fails to load. A
/// string plus this check gets the safety without the cycle.
///
public IReadOnlyList Validate()
{
var problems = new List();
foreach (var level in Levels.Where(l => l != null))
{
if (string.IsNullOrWhiteSpace(level.ScenePath))
problems.Add($"level '{level.Id}' has no scene path");
else if (!ResourceLoader.Exists(level.ScenePath))
problems.Add($"level '{level.Id}' points at '{level.ScenePath}', which does not exist");
}
return problems;
}
public LevelResource Find(LevelId id) =>
Levels.FirstOrDefault(level => level != null && level.Id == id.Value);
/// Loads a Level's scene on demand. Null when the Level is not catalogued.
public PackedScene SceneFor(LevelId id)
{
var path = Find(id)?.ScenePath;
return string.IsNullOrWhiteSpace(path) ? null : ResourceLoader.Load(path);
}
public string DisplayNameKeyFor(LevelId id) => Find(id)?.LevelName ?? string.Empty;
}