using System; using System.IO; using System.Collections.Generic; using System.Diagnostics; using System.Text.RegularExpressions; using DesertPaintCodex.Models; using DesertPaintCodex.Util; namespace DesertPaintCodex.Services { internal static class ReagentService { private static readonly Regex _ppReagentRegex = new(@"(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+)\s*\|\s*(?\d+)\s*\|\s*(?[YN])\s*\|\s*(?(bulk|normal))\s*\|\s*(?\d+).*"); private static readonly Regex _ppCatalystRegex = new(@"(?(\w*\s)*\w+\w+)\s*\|\s*catalyst\s*\|\s*(?\d+)\s*\|\s*(?[YN])\s*\|\s*(?(bulk|normal)).*"); private static readonly Regex _internalReagentRegex = new(@"(?(\w*\s)*\w+)\s*\|\s*(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+).*"); private static readonly Regex _internalCatalystRegex = new(@"(?(\w+\s)*\w+)\s*\|\s*(?\w+)\s*\|\s*catalyst.*"); private static readonly Regex _renameHeader = new(@"#\s*(?
\w*)"); private static readonly Regex _renameRegex = new(@"(?(\w+\s)*\w+)\s*\|\s*(?(\w+\s)*\w+)"); private static readonly Dictionary _reagents = new(); private static readonly Dictionary _ppToInternalName = new(); // pp name to our name private static readonly Dictionary _ppRenames = new(); private static readonly Dictionary _internalRenames = new(); private static bool _initialized = false; public static List Names { get; } = new(); private static void Initialize() { if (_initialized) return; string? reagentsPath = FileUtils.FindApplicationResourceFile("ingredients.txt"); Debug.Assert(reagentsPath != null); Load(reagentsPath); string? renamesPath = FileUtils.FindApplicationResourceFile("renames.txt"); if (renamesPath != null) { LoadAliases(renamesPath); } _initialized = true; } // Loads reagent name/colors private static void Load(string file) { _reagents.Clear(); _ppToInternalName.Clear(); Names.Clear(); int index = 0; using StreamReader reader = new(file); string? line; while ((line = reader.ReadLine()) != null) { Match match = _internalReagentRegex.Match(line); if (match.Success) { string name = match.Groups["name"].Value; string ppname = match.Groups["ppname"].Value; Debug.WriteLine("Adding reagent[" + name + "] from line: [" + line + "]"); _reagents.Add(name, new Reagent(name, ppname, byte.Parse(match.Groups["red"].Value), byte.Parse(match.Groups["green"].Value), byte.Parse(match.Groups["blue"].Value), index++)); // nameStore.AppendValues(name); Names.Add(name); _ppToInternalName.Add(ppname, name); } else { match = _internalCatalystRegex.Match(line); if (!match.Success) continue; string name = match.Groups["name"].Value; string ppname = match.Groups["ppname"].Value; _reagents.Add(name, new Reagent(ppname, ppname, index++)); // nameStore.AppendValues(name); Names.Add(name); _ppToInternalName.Add(ppname, name); } } } private static void LoadAliases(string file) { _ppRenames.Clear(); _internalRenames.Clear(); Dictionary? renameDict = null; using StreamReader reader = new(file); string? line; while ((line = reader.ReadLine()) != null) { Match match = _renameHeader.Match(line); if (match.Success) { string header = match.Groups["header"].Value; renameDict = header switch { "PracticalPaint" => _ppRenames, "Internal" => _internalRenames, _ => renameDict }; } if (renameDict == null) continue; match = _renameRegex.Match(line); if (!match.Success) continue; string oldName = match.Groups["oldName"].Value; string newName = match.Groups["newName"].Value; renameDict.Add(oldName, newName); } } public static void LoadProfileReagents(string file) { Initialize(); using StreamReader reader = new(file); string? line; while ((line = reader.ReadLine()) != null) { Match match = _ppReagentRegex.Match(line); if (match.Success) { string ppname = match.Groups["name"].Value; if (_ppRenames.TryGetValue(ppname, out string? rename)) { ppname = rename; // Apply rename. } if (_ppToInternalName.TryGetValue(ppname, out string? name)) { Reagent reagent = GetReagent(name); if (reagent.IsCatalyst) continue; reagent.Enabled = match.Groups["enabled"].Value.Equals("Y"); reagent.Cost = uint.Parse(match.Groups["cost"].Value); reagent.RecipeMax = uint.Parse(match.Groups["max"].Value); } } else { match = _ppCatalystRegex.Match(line); if (!match.Success) continue; string ppname = match.Groups["name"].Value; if (_ppRenames.TryGetValue(ppname, out string? rename)) { ppname = rename; // Apply rename. } if (_ppToInternalName.TryGetValue(ppname, out string? name)) { Reagent reagent = GetReagent(name); if (reagent is not {IsCatalyst: true}) continue; reagent.Enabled = match.Groups["enabled"].Value.Equals("Y"); reagent.Cost = uint.Parse(match.Groups["cost"].Value); } } } } public static void SaveProfileReagents(string file) { Initialize(); using StreamWriter writer = new(file); writer.WriteLine("// Ingredients are in the form:"); writer.WriteLine("// Name | RGB values | cost | enabled (Y/N) | bulk/normal | max items per paint (1-20)"); writer.WriteLine("//"); writer.WriteLine("// It is recommended to only change the cost value"); writer.WriteLine("// It is not recommended to set many of the ingredients above 10 per paint"); List sortedReagents = new(_reagents.Count); sortedReagents.AddRange(_reagents.Values); sortedReagents.Sort((x, y) => ((x.IsCatalyst && !y.IsCatalyst) ? 1 : ((y.IsCatalyst && !x.IsCatalyst) ? -1 : string.Compare(x.PracticalPaintName, y.PracticalPaintName, StringComparison.InvariantCulture)))); foreach (Reagent reagent in sortedReagents) { if (!reagent.IsCatalyst) { writer.WriteLine("{0,-10} | {1,3}, {2,3}, {3,3} | {4,7} | {5} | {6} | {7}", reagent.PracticalPaintName, reagent.Color?.Red, reagent.Color?.Blue, reagent.Color?.Green, reagent.Cost, reagent.Enabled ? "Y" : "N", reagent.RecipeMax >= 10 ? " bulk" : "normal", reagent.RecipeMax); } else { writer.WriteLine("{0,-10} | catalyst | {1,7} | {2} | normal | 1", reagent.PracticalPaintName, reagent.Cost, reagent.Enabled ? "Y" : "N"); } } } public static void InitializeReactions(ReactionSet reactions) { Initialize(); foreach (KeyValuePair pair1 in _reagents) { foreach (KeyValuePair pair2 in _reagents) { if (pair1.Key != pair2.Key) { reactions.Set(pair1.Value, pair2.Value, null); } } } } public static Reagent GetReagent(string reagentName) { Initialize(); Reagent? returnVal = GetReagentByInternalName(reagentName); if (returnVal != null) return returnVal; returnVal = GetReagentByPPName(reagentName); if (returnVal != null) return returnVal; string? rename = null; if (_internalRenames.TryGetValue(reagentName, out rename)) { returnVal = GetReagentByInternalName(rename); if (returnVal != null) return returnVal; } if (_ppRenames.TryGetValue(reagentName, out rename)) { returnVal = GetReagentByPPName(rename); if (returnVal != null) return returnVal; } Debug.Assert(returnVal != null); return returnVal; } private static Reagent? GetReagentByInternalName(string internalName) { return _reagents.TryGetValue(internalName, out Reagent? returnVal) ? returnVal : null; } private static Reagent? GetReagentByPPName(string ppName) { if (!_ppToInternalName.TryGetValue(ppName, out string? otherName)) return null; return _reagents.TryGetValue(otherName, out Reagent? returnVal) ? returnVal : null; } } }