using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using GameOfLife.Entities; namespace GameOfLife.IO { public class RleDecoder { public Pattern Pattern { get; } public PatternMetadata Metadata { get; } public RleDecoder(string filename) : this(new FileInfo(filename)) { } public RleDecoder(FileSystemInfo fi) : this(GetLines(fi)) { } public RleDecoder(IEnumerable encodedRle) { var groups = (from line in encodedRle let trimmed = line.Trim() where !string.IsNullOrEmpty(trimmed) group line by line.StartsWith("#") into g select g ).ToArray(); var comments = groups.First(g => g.Key); var nonComments = groups.First(g => !g.Key).ToArray(); Metadata = new RlePatternMetaData(nonComments.First(), comments); var rleData = string.Join("", nonComments.Skip(1)); Pattern = Pattern.Extract(ExpandRle(rleData).ToArray()); } /// /// Returns RLE encoded string into decoded expansion, breaking lines into an enumeration /// private static IEnumerable ExpandRle(string rleData) { if (!rleData.Contains("!")) throw new ArgumentException("RLE pattern did not contain terminating character '!'"); var encodedRle = rleData.Substring(0, rleData.IndexOf("!", StringComparison.Ordinal)); var rleDecodingRegex = new Regex(@"(?\d*)(?[a-z$])"); var matches = rleDecodingRegex.Matches(encodedRle); var decoded = string.Empty; foreach (Match match in matches) { var countStr = match.Groups["count"].Value; var count = string.IsNullOrEmpty(countStr) ? 1 : int.Parse(countStr); var c = match.Groups["char"].Value.First(); for (var i = 0; i < count; i++) { switch (c) { case '$': yield return decoded; decoded = string.Empty; break; default: decoded += c; break; } } } yield return decoded; } private static IEnumerable GetLines(FileSystemInfo fi) { if (!fi.Exists) throw new FileNotFoundException(fi.FullName); return File.ReadAllLines(fi.FullName); } } }