private static string FunctionSubString(Match m, NamePatternParameters p)
        {
            // check param number: 1: substring, 2: p1, 3: pos, 4: length
            if (!int.TryParse(m.Groups[3].Value, out var pos))
            {
                return(m.Groups[2].Value);
            }

            bool fromEnd = pos < 0;

            pos = Math.Min(Math.Abs(pos), m.Groups[2].Value.Length);
            if (string.IsNullOrEmpty(m.Groups[4].Value))
            {
                if (fromEnd)
                {
                    return(m.Groups[2].Value.Substring(m.Groups[2].Value.Length - pos));
                }
                return(m.Groups[2].Value.Substring(pos));
            }
            else
            {
                int length = Math.Min(Convert.ToInt32(Convert.ToInt32(m.Groups[4].Value)), fromEnd ? pos : m.Groups[2].Value.Length - pos);
                if (fromEnd)
                {
                    return(m.Groups[2].Value.Substring(m.Groups[2].Value.Length - pos, length));
                }
                return(m.Groups[2].Value.Substring(pos, length));
            }
        }
 private static string FunctionIf(Match m, NamePatternParameters p)
 {
     // check if Group2 !isNullOrWhiteSpace
     // Group3 contains the result if true
     // Group4 (optional) contains the result if false
     return(string.IsNullOrWhiteSpace(m.Groups[2].Value) ? m.Groups[4].Value : m.Groups[3].Value);
 }
 private static string FunctionReplace(Match m, NamePatternParameters p)
 {
     // parameter: 1: replace, 2: text, 3: find, 4: replace
     if (string.IsNullOrEmpty(m.Groups[2].Value) ||
         string.IsNullOrEmpty(m.Groups[3].Value))
     {
         return(m.Groups[2].Value);
     }
     return(m.Groups[2].Value.Replace(m.Groups[3].Value.Replace("&nbsp;", " "), m.Groups[4].Value.Replace("&nbsp;", " ")));
 }
        internal static string ResolveFunction(Match match, NamePatternParameters parameters)
        {
            var functionName = match.Groups[1].Value.ToLower();

            if (!string.IsNullOrEmpty(functionName) && _functions.TryGetValue(functionName, out var func))
            {
                return(func(match, parameters));
            }

            return(string.Empty);
        }
 private static string FunctionCustomReplace(Match m, NamePatternParameters p)
 {
     // parameter: 1: customreplace, 2: key, 3: return if key not available
     if (p.CustomReplacings == null ||
         string.IsNullOrEmpty(m.Groups[2].Value) ||
         !p.CustomReplacings.ContainsKey(m.Groups[2].Value))
     {
         return(m.Groups[3].Value);
     }
     return(p.CustomReplacings[m.Groups[2].Value]);
 }
        private static string FunctionIndexOf(Match m, NamePatternParameters p)
        {
            // parameter: 1: source string, 2: string to find
            if (string.IsNullOrEmpty(m.Groups[2].Value) || string.IsNullOrEmpty(m.Groups[3].Value))
            {
                return(string.Empty);
            }
            int index = m.Groups[2].Value.IndexOf(m.Groups[3].Value);

            return(index >= 0 ? index.ToString() : string.Empty);
        }
        private static string FunctionCasing(Match m, NamePatternParameters p)
        {
            // parameter: 1: casing, 2: text, 3: U for UPPER, L for lower, T for Title
            switch (m.Groups[3].Value.ToLower())
            {
            case "u": return(m.Groups[2].Value.ToUpperInvariant());

            case "l": return(m.Groups[2].Value.ToLowerInvariant());

            case "t": return(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(m.Groups[2].Value));
            }
            return(ParametersInvalid($"casing expects 'U', 'L' or 'T', given is '{m.Groups[3].Value}'", m.Groups[0].Value, p.DisplayError));
        }
        private static string FunctionPadRight(Match m, NamePatternParameters p)
        {
            // check param number: 1: padright, 2: p1, 3: desired length, 4: padding char

            var padLen  = Convert.ToInt32(m.Groups[3].Value);
            var padChar = m.Groups[4].Value;

            if (!string.IsNullOrEmpty(padChar))
            {
                return(m.Groups[2].Value.PadRight(padLen, padChar[0]));
            }
            return(ParametersInvalid($"No padding char given.", m.Groups[0].Value, p.DisplayError));
        }
        private static string FunctionIfExpr(Match m, NamePatternParameters p)
        {
            // tries to evaluate the expression
            // possible operators are ==, !=, <, >, =<, =>
            var match = Regex.Match(m.Groups[2].Value, @"\A\s*(\d+(?:\.\d*)?)\s*(==|!=|<|<=|>|>=)\s*(\d+(?:\.\d*)?)\s*\Z");

            if (match.Success &&
                double.TryParse(match.Groups[1].Value, out double d1) &&
                double.TryParse(match.Groups[3].Value, out double d2)
                )
            {
                switch (match.Groups[2].Value)
                {
                case "==": return(d1 == d2 ? m.Groups[3].Value : m.Groups[4].Value);

                case "!=": return(d1 != d2 ? m.Groups[3].Value : m.Groups[4].Value);

                case "<": return(d1 < d2 ? m.Groups[3].Value : m.Groups[4].Value);

                case "<=": return(d1 <= d2 ? m.Groups[3].Value : m.Groups[4].Value);

                case ">": return(d1 > d2 ? m.Groups[3].Value : m.Groups[4].Value);

                case ">=": return(d1 >= d2 ? m.Groups[3].Value : m.Groups[4].Value);
                }
            }
            else
            {
                // compare the values as strings
                match = Regex.Match(m.Groups[2].Value, @"\A\s*(.*?)\s*(==|!=|<=|<|>=|>)\s*(.*?)\s*\Z");
                if (match.Success)
                {
                    int stringComparingResult = match.Groups[1].Value.CompareTo(match.Groups[3].Value);
                    switch (match.Groups[2].Value)
                    {
                    case "==": return(stringComparingResult == 0 ? m.Groups[3].Value : m.Groups[4].Value);

                    case "!=": return(stringComparingResult != 0 ? m.Groups[3].Value : m.Groups[4].Value);

                    case "<": return(stringComparingResult < 0 ? m.Groups[3].Value : m.Groups[4].Value);

                    case "<=": return(stringComparingResult <= 0 ? m.Groups[3].Value : m.Groups[4].Value);

                    case ">": return(stringComparingResult > 0 ? m.Groups[3].Value : m.Groups[4].Value);

                    case ">=": return(stringComparingResult >= 0 ? m.Groups[3].Value : m.Groups[4].Value);
                    }
                }
            }
            return(ParametersInvalid($"The expression for ifexpr invalid: \"{m.Groups[2].Value}\"", m.Groups[0].Value, p.DisplayError));
        }
        private static string FormatString(string s, string formatString, Match m, NamePatternParameters p, bool isInteger = false)
        {
            if (string.IsNullOrEmpty(formatString))
            {
                return(ParametersInvalid("No Format string given", m.Groups[0].Value, p.DisplayError));
            }

            if (string.IsNullOrEmpty(s))
            {
                return(string.Empty);
            }

            return(isInteger ? Convert.ToInt32(s).ToString(formatString) : Convert.ToDouble(s).ToString(formatString));
        }
        private static string FunctionFloatDiv(Match m, NamePatternParameters p)
        {
            // returns an float after dividing the parsed number
            // parameter: 1: div, 2: number, 3: divided by 4: format string
            double dividend = double.Parse(m.Groups[2].Value);
            double divisor  = double.Parse(m.Groups[3].Value);

            if (divisor > 0)
            {
                return(((dividend / divisor)).ToString(m.Groups[4].Value));
            }
            else
            {
                return(ParametersInvalid("Division by 0", m.Groups[0].Value, p.DisplayError));
            }
        }
        private static string FunctionDiv(Match m, NamePatternParameters p)
        {
            // returns an integer after dividing the parsed number
            // parameter: 1: div, 2: number, 3: divided by
            double number = double.Parse(m.Groups[2].Value);
            double div    = double.Parse(m.Groups[3].Value);

            if (div > 0)
            {
                return(((int)(number / div)).ToString());
            }
            else
            {
                return(ParametersInvalid("Division by 0", m.Groups[0].Value, p.DisplayError));
            }
        }
        private static string FunctionExpr(Match m, NamePatternParameters p)
        {
            // tries to calculate the result of the expression
            // possible operators are +, -, *, /
            var match = Regex.Match(m.Groups[2].Value, @"\A\s*(\d+(?:\.\d*)?)\s*(\+|\-|\*|\/)\s*(\d+(?:\.\d*)?)\s*\Z");

            if (match.Success &&
                double.TryParse(match.Groups[1].Value, out var d1) &&
                double.TryParse(match.Groups[3].Value, out var d2)
                )
            {
                switch (match.Groups[2].Value)
                {
                case "+": return((d1 + d2).ToString());

                case "-": return((d1 - d2).ToString());

                case "*": return((d1 * d2).ToString());

                case "/": return(d2 == 0 ? "divByZero" : (d1 / d2).ToString());
                }
            }
            return(ParametersInvalid($"The expression for expr is invalid: \"{m.Groups[2].Value}\"", m.Groups[0].Value, p.DisplayError));
        }
        private static string FunctionColor(Match m, NamePatternParameters p)
        {
            // parameter 1: region id (0,...,5), 2: if not empty, the color name instead of the numerical id is returned, 3: if not empty, the function will return also a value even if the color region is not used on that species.
            if (!int.TryParse(m.Groups[2].Value, out int regionId) ||
                regionId < 0 || regionId > 5)
            {
                return(ParametersInvalid("color region id has to be a number in the range 0 - 5", m.Groups[0].Value, p.DisplayError));
            }

            if (!p.Creature.Species.EnabledColorRegions[regionId] &&
                string.IsNullOrWhiteSpace(m.Groups[4].Value))
            {
                return(string.Empty); // species does not use this region and user doesn't want it (param 3 is empty)
            }
            if (p.Creature.colors == null)
            {
                return(string.Empty);                           // no color info
            }
            if (string.IsNullOrWhiteSpace(m.Groups[3].Value))
            {
                return(p.Creature.colors[regionId].ToString());
            }
            return(CreatureColors.CreatureColorName(p.Creature.colors[regionId]));
        }
 private static string FunctionLen(Match m, NamePatternParameters p)
 {
     // returns the length of the parameter
     return(m.Groups[2].Value.Length.ToString());
 }
 private static string FunctionFormat(Match m, NamePatternParameters p)
 {
     // check param number: 1: format, 2: p1, 3: formatString
     return(FormatString(m.Groups[2].Value, m.Groups[3].Value, m, p));
 }
 private static string FunctionTime(Match m, NamePatternParameters p)
 {
     // parameter: 1: time, 2: format
     return(DateTime.Now.ToString(m.Groups[2].Value));
 }
 private static string FunctionFormatInt(Match m, NamePatternParameters p)
 {
     return(FormatString(m.Groups[2].Value, m.Groups[3].Value, m, p, true));
 }