Files @ 70b1de28b2a2
Branch filter:

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

Jason Maltzen
Re-enable the ability to save debug screenshots based on a setting value to help debug reaction capturing. Update the README to correctly reflect the debug.screenshot setting name, location of the settings file, and removal of the old debug menu.
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;
        }
    }
}