using System.Collections.Generic;
using Cthangover.Mods.Dialog.Scenarios;
using Cthangover.Core.Scenarios;
using Cthangover.Core.Factories.Impls;
using Cthangover.Core.UI.Theming;
using Godot;
namespace Cthangover.Tools.Services
{
///
/// Static service providing scenario DSL syntax highlighting, tokenisation,
/// and command-line parsing. produces a
/// for the scenario editor's
/// . tokenises a
/// scenario command line into a command name, positional arguments, and
/// named (key=value) parameters — used by
/// for reference analysis.
///
public static class ScenarioSyntaxService
{
///
/// Builds a for scenario DSL syntax.
/// Comments (lines starting with #) are green, strings are bright
/// gold, command names are blue, and argument keywords (e.g. key,
/// wait, quest_id) are amber — all from .
///
public static CodeHighlighter CreateHighlighter()
{
var hl = new CodeHighlighter();
hl.AddColorRegion("#", null, UiTokens.Success, true);
hl.AddColorRegion("\"", "\"", UiTokens.AccentBright, false);
hl.NumberColor = UiTokens.Info;
hl.SymbolColor = UiTokens.Muted;
var cmdColor = UiTokens.Info;
hl.KeywordColors = new Godot.Collections.Dictionary();
foreach (var cmd in ScenarioCommandStrategyFactory.GetAllCommandNames())
hl.KeywordColors[cmd] = cmdColor;
foreach (var cmd in new[] {
"option", "select"
}) hl.KeywordColors[cmd] = cmdColor;
var argColor = UiTokens.Accent;
foreach (var kw in new[] {
"key", "first", "second", "hide_color", "wait", "speed", "hidden",
"sprites", "loop", "target", "quest_id", "show", "hide", "duration"
}) hl.KeywordColors[kw] = argColor;
return hl;
}
///
/// Splits a line into tokens, handling quoted strings (with backslash
/// escaping) and whitespace. Returns all non-whitespace tokens in order.
///
public static List Tokenize(string line)
{
var result = new List();
var i = 0;
while (i < line.Length)
{
if (char.IsWhiteSpace(line[i])) { i++; continue; }
if (line[i] == '"')
{
i++;
var start = i;
while (i < line.Length && line[i] != '"')
{
if (line[i] == '\\' && i + 1 < line.Length) i++;
i++;
}
result.Add(line.Substring(start, i - start));
if (i < line.Length) i++;
continue;
}
var tok = i;
while (i < line.Length && !char.IsWhiteSpace(line[i])) i++;
result.Add(line.Substring(tok, i - tok));
}
return result;
}
/// Strips surrounding double quotes from a value if present.
public static string StripQuotes(string val)
{
if (val.Length >= 2 && val.StartsWith("\"") && val.EndsWith("\""))
return val.Substring(1, val.Length - 2);
return val;
}
///
/// Parses a scenario command line into its components. Tokenises via
/// , then extracts the first token as the command
/// name (lowercased), remaining bare tokens as positional arguments, and
/// key=value pairs as named arguments. The -> arrow token
/// is consumed but discarded.
///
/// A single scenario command line (trimmed).
/// The command name, lowercased.
/// Positional arguments in order.
/// Named arguments keyed by lowercased name.
public static void ExtractCommandParams(string line, out string cmd, out List positional, out Dictionary named)
{
positional = new List();
named = new Dictionary(System.StringComparer.OrdinalIgnoreCase);
cmd = "";
var tokens = Tokenize(line);
if (tokens.Count == 0)
return;
cmd = tokens[0].ToLowerInvariant();
for (int t = 1; t < tokens.Count; t++)
{
var tk = tokens[t];
if (tk == "->") { t++; continue; }
var eq = tk.IndexOf('=');
if (eq > 0)
named[tk.Substring(0, eq).ToLowerInvariant()] = StripQuotes(tk.Substring(eq + 1));
else
positional.Add(tk);
}
}
}
}