namespace AcDream.App.Studio;
///
/// Parsed options for the acdream UI Studio standalone tool.
/// Constructed by from the command-line tokens that follow
/// the ui-studio dispatch token.
///
public sealed record StudioOptions(
string DatDir,
uint? LayoutId,
string? MarkupPath,
string? DumpSlug = null,
string? DumpFile = null,
string? ScreenshotPath = null)
{
///
/// Parse studio options from the args that come AFTER the ui-studio token.
///
/// Positional (first non-flag arg): dat directory. Falls back to
/// ACDREAM_DAT_DIR when omitted.
/// --layout 0xNNNN: hex LayoutDesc dat id to preview.
/// --markup <path>: path to a KSML markup file (Task 6, unsupported for now).
/// --dump <slug>: load a panel from the retail UI dump JSON by slug
/// (e.g. inventory, radar, toolbar). Static mockup — no controllers.
/// --dump-file <path>: override the default dump file path
/// (docs/research/2026-06-25-retail-ui-layout-dump.json from the solution root).
/// Only meaningful when --dump is also given.
/// --screenshot <path>: headless mode — render the loaded panel to a PNG
/// at and exit without showing an interactive window.
/// Combines with --dump or --layout.
/// When neither --layout, --markup, nor --dump is given the
/// default layout 0x2100006C (vitals) is used.
///
public static StudioOptions Parse(string[] args)
{
string? datDir = null;
uint? layoutId = null;
string? markupPath = null;
string? dumpSlug = null;
string? dumpFile = null;
string? screenshotPath = null;
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--layout" && i + 1 < args.Length)
{
var raw = args[++i];
// Accept 0xNNNN or plain hex.
if (raw.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
raw = raw[2..];
if (uint.TryParse(raw, System.Globalization.NumberStyles.HexNumber, null, out var id))
layoutId = id;
}
else if (args[i] == "--markup" && i + 1 < args.Length)
{
markupPath = args[++i];
}
else if (args[i] == "--dump" && i + 1 < args.Length)
{
dumpSlug = args[++i];
}
else if (args[i] == "--dump-file" && i + 1 < args.Length)
{
dumpFile = args[++i];
}
else if (args[i] == "--screenshot" && i + 1 < args.Length)
{
screenshotPath = args[++i];
}
else if (!args[i].StartsWith('-'))
{
datDir ??= args[i];
}
}
// Fall back to ACDREAM_DAT_DIR when no positional dat dir was given.
datDir ??= Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR");
if (string.IsNullOrWhiteSpace(datDir))
throw new InvalidOperationException(
"ui-studio: dat directory required — pass as first arg or set ACDREAM_DAT_DIR.");
// Default layout: vitals (0x2100006C), unless a dump slug or markup is requested.
if (layoutId is null && markupPath is null && dumpSlug is null)
layoutId = 0x2100006Cu;
return new StudioOptions(datDir, layoutId, markupPath, dumpSlug, dumpFile, screenshotPath);
}
///
/// Resolve the dump file path for this session:
///
/// - if explicitly set.
/// - Otherwise <solutionRoot>/docs/research/2026-06-25-retail-ui-layout-dump.json.
///
/// Returns null when neither the override nor the default file exists.
///
public string? ResolveDumpFile()
{
if (!string.IsNullOrEmpty(DumpFile))
return DumpFile;
// Walk up from the App binary to the solution root (same approach as
// ConformanceDats.SolutionRoot in the test project).
var dir = AppContext.BaseDirectory;
while (!string.IsNullOrEmpty(dir))
{
var candidate = Path.Combine(dir, "docs", "research",
"2026-06-25-retail-ui-layout-dump.json");
if (File.Exists(candidate))
return candidate;
dir = Path.GetDirectoryName(dir);
}
return null;
}
}