namespace Ficdown.Parser.Player { using System.Collections.Generic; using System.Linq; using Model.Story; using Model.Traverser; using Parser; internal class GameTraverser { private readonly StateManager _manager; private readonly Queue _processingQueue; private readonly IDictionary _processed; public GameTraverser(Story story) { _manager = new StateManager(story); _processingQueue = new Queue(); _processed = new Dictionary(); } public IEnumerable Enumerate() { // generate comprehensive enumeration var initial = _manager.InitialState; _processingQueue.Enqueue(new StateQueueItem { Page = initial, AffectedStates = new List {initial.AffectedState} }); while (_processingQueue.Count > 0) { var state = _processingQueue.Dequeue(); if (!_processed.ContainsKey(state.Page.UniqueHash)) { _processed.Add(state.Page.UniqueHash, state.Page); ProcessState(state); } } // compress redundancies return _processed.Values; } private void ProcessState(StateQueueItem currentState) { var states = new HashSet(); var anchors = Utilities.ParseAnchors(currentState.Page.Scene.Description); var conditionals = anchors.SelectMany( a => a.Href.Conditions != null ? a.Href.Conditions.Select(c => c.Key) : new string[] {}) .Distinct() .ToArray(); var hasFirstSeen = RegexLib.BlockQuotes.IsMatch(currentState.Page.Scene.Description); foreach (var affected in currentState.AffectedStates) { // signal to previous scenes that this scene's used conditionals are important foreach (var conditional in conditionals) _manager.ToggleStateOn(affected, conditional); // signal to previous scenes if this scene has first-seen text if (hasFirstSeen) _manager.ToggleSeenSceneOn(affected, currentState.Page.Scene.Id); } foreach (var anchor in anchors.Where(a => a.Href.Target != null || a.Href.Toggles != null)) { var newState = _manager.ResolveNewState(anchor, currentState.Page); if (!currentState.Page.Links.ContainsKey(anchor.Original)) currentState.Page.Links.Add(anchor.Original, newState.UniqueHash); if (!states.Contains(newState.UniqueHash) && !_processed.ContainsKey(newState.UniqueHash)) { states.Add(newState.UniqueHash); var newAffected = new List(currentState.AffectedStates); newAffected.Add(newState.AffectedState); _processingQueue.Enqueue(new StateQueueItem {Page = newState, AffectedStates = newAffected}); } } } } }