namespace Ficdown.Parser.Parser { using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Model.Parser; internal class Utilities { public static Utilities GetInstance(string blockName, int lineNumber) { return new Utilities { _blockName = blockName, _lineNumber = lineNumber }; } public static Utilities GetInstance(string blockName) { return new Utilities { _blockName = blockName }; } protected string _blockName; protected int? _lineNumber; public string NormalizeString(string raw) { return Regex.Replace(Regex.Replace(raw.ToLower(), @"^\W+|\W+$", string.Empty), @"\W+", "-"); } private Href ParseHref(string href, int lineNumber, int colNumber) { var match = RegexLib.Href.Match(href); if (match.Success) { var ttstr = match.Groups["target"].Value; var cstr = match.Groups["conditions"].Value; var tstr = match.Groups["toggles"].Value; return new Href { Original = href, Target = !string.IsNullOrEmpty(ttstr) ? ttstr.TrimStart('/') : null, Conditions = !string.IsNullOrEmpty(cstr) ? new List(cstr.TrimStart('?').Split('&').Select(c => c.Trim().ToLower())) .ToDictionary(c => c.TrimStart('!'), c => !c.StartsWith("!")) : null, Toggles = !string.IsNullOrEmpty(tstr) ? new List(tstr.TrimStart('#').Split('+').Select(t => t.Trim().ToLower())).ToArray() : null }; } throw new FicdownException(_blockName, string.Format("Invalid href: {0}", href), lineNumber, colNumber); } public Anchor ParseAnchor(string anchorText, int lineNumber, int colNumber) { var match = RegexLib.Anchors.Match(anchorText); if (!match.Success) throw new FicdownException(_blockName, string.Format("Invalid anchor: {0}", anchorText), lineNumber, colNumber); return MatchToAnchor(match, lineNumber, colNumber); } private void PosFromIndex(string text, int index, out int line, out int col) { line = 1; col = 1; for (int i = 0; i <= index - 1; i++) { col++; if (text[i] == '\n') { line++; col = 1; } } } public IList ParseAnchors(string text) { var matches = RegexLib.Anchors.Matches(text); return matches.Cast().Select(m => { int line, col; PosFromIndex(text, m.Index, out line, out col); if(_lineNumber.HasValue) line += _lineNumber.Value; return MatchToAnchor(m, line, col); }).ToList(); } private Anchor MatchToAnchor(Match match, int lineNumber, int colNumber) { var astr = match.Groups["anchor"].Value; var txstr = match.Groups["text"].Value; var ttstr = match.Groups["title"].Success ? match.Groups["title"].Value : null; var hrefstr = match.Groups["href"].Value; if (hrefstr.StartsWith(@"""")) { ttstr = hrefstr.Trim('"'); hrefstr = string.Empty; } return new Anchor { Original = !string.IsNullOrEmpty(astr) ? astr : null, Text = !string.IsNullOrEmpty(txstr) ? txstr : null, Title = ttstr, Href = ParseHref(hrefstr, lineNumber, colNumber), LineNumber = lineNumber, ColNumber = colNumber }; } public IDictionary ParseConditionalText(Anchor anchor) { var match = RegexLib.ConditionalText.Match(anchor.Text); if (!match.Success) throw new FicdownException(_blockName, string.Format(@"Invalid conditional text: {0}", anchor.Text), anchor.LineNumber, anchor.ColNumber); return new Dictionary { {true, match.Groups["true"].Value}, {false, match.Groups["false"].Value} }; } public bool ConditionsMet(IDictionary playerState, IDictionary conditions) { return conditions.All( c => (!c.Value && (!playerState.ContainsKey(c.Key) || !playerState[c.Key])) || (c.Value && (playerState.ContainsKey(c.Key) && playerState[c.Key]))); } } }