Files @ 4778889395e8
Branch filter:

Location: ATITD-Tools/Desert-Paint-Codex/Services/ReagentService.cs

Jason Maltzen
Change the displayed permeutation count to display using locale-specific number formatting (with comma / dot separators). Fix a crash when starting a new round of recipe generation after recipe generation completed during a previous run.
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(@"(?<name>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+)\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal))\s*\|\s*(?<max>\d+).*");
        private static readonly Regex _ppCatalystRegex       = new(@"(?<name>(\w*\s)*\w+\w+)\s*\|\s*catalyst\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal)).*");
        private static readonly Regex _internalReagentRegex  = new(@"(?<name>(\w*\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+).*");
        private static readonly Regex _internalCatalystRegex = new(@"(?<name>(\w+\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*catalyst.*");
        private static readonly Regex _renameHeader          = new(@"#\s*(?<header>\w*)");
        private static readonly Regex _renameRegex           = new(@"(?<oldName>(\w+\s)*\w+)\s*\|\s*(?<newName>(\w+\s)*\w+)");

        private static readonly Dictionary<string, Reagent> _reagents   = new();
        private static readonly Dictionary<string, string>  _ppToInternalName = new(); // pp name to our name
        private static readonly Dictionary<string, string> _ppRenames = new();
        private static readonly Dictionary<string, string> _internalRenames = new();
        
        private static bool _initialized = false;

        public static List<string> 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<string, string>? 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<Reagent> 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<string, Reagent> pair1 in _reagents)
            {
                foreach (KeyValuePair<string, Reagent> 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;
        }
    }
}