/**
 * @file wordStringParsing.js
 * @module wordStringParsing
 * @description Contains all system defined business rules for parsing words and lists.
 * @requires module:ruleParsing
 * @requires module:configurator
 * @requires module:loggers
 * @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
 * @requires {@link https://mathjs.org/index.html|math}
 * @requires {@link https://www.npmjs.com/package/path|path}
 * @author Seth Hollingsead
 * @date 2022/04/25
 * @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
 */

// Internal imports
import ruleParsing from '../ruleParsing.js';
import configurator from '../../../executrix/configurator.js';
import loggers from '../../../executrix/loggers.js';
// External imports
import hayConst from '@haystacks/constants';
import * as math from 'mathjs';
import path from 'path';

const {bas, biz, cfg, gen, msg, sys, wrd} = hayConst;
const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
// framework.businessRules.rules.stringParsing.wordStringParsing.
const namespacePrefix = wrd.cframework + bas.cDot + sys.cbusinessRules + bas.cDot + wrd.crules + bas.cDot + wrd.cstring + wrd.cParsing + bas.cDot + baseFileName + bas.cDot;

/**
 * @function isStringCamelCase
 * @description Determines if an input string is a camel case string or not.
 * @param {string} inputData The string that should be checked for the camel case qualifications.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {boolean} True or False to indicate if the string is camel case or not.
 * @author Seth Hollingsead
 * @date 2022/01/23
 * @NOTE Even if we have an all upper case acronym at the end fo the camel case string,
 * the string itself is still considered camel case.
 * Valid Examples:
 * myParsedXML
 * fireflyWonder1996
 * wonderWomand1984
 * covidMedicalCase
 * aBc
 */
async function isStringCamelCase(inputData, inputMetaData) {
  let functionName = isStringCamelCase.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = false;
  if (inputData) {
    let foundFirstCapitalLetter = false;
    // First make sure the string meets the basic qualifications of a camel case string.
    // 1. Does not contain underscore or dash word separators.
    // 2. Contains at least 1 lower case letter or more.
    // 3. Contains at least 1 upper case letter or more.
    // 4. Has a lower case or upper case first letter of the first word.
    let doesContainUpperCaseCharacter = await ruleParsing.processRulesInternal([inputData, ''], [biz.cdoesStringContainUpperCaseCharacter]);
    let doesContainLowerCaseCharacter = await ruleParsing.processRulesInternal([inputData, ''], [biz.cdoesStringContainLowerCaseCharacter]);
    let isFirstCharUpperCase = await ruleParsing.processRulesInternal([inputData, ''], [biz.cisFirstCharacterUpperCase]);
    let isFirstCharLowerCase = await ruleParsing.processRulesInternal([inputData, ''], [biz.cisFirstCharacterLowerCase]);
    if (!inputData.match(/[\s_-]/g) && doesContainUpperCaseCharacter &&
    doesContainLowerCaseCharacter && (isFirstCharUpperCase || isFirstCharLowerCase)) {
      for (let i = 1; i < inputData.length; i++) {
        // Now check or the final qualification:
        // 3. Ensure that upper case letters are separated by lower case letters
        // (numbers also allowed, but there should be at least some lower case letters)
        // NOTE: This for-loop is how we iterate over the characters of the string.

        // First we need to ok for the first upper case letter.
        if (foundFirstCapitalLetter === false) {
          if (gen.cUpperCaseEnglishAlphabet.includes(inputData.charAt(i))) {
            // Found an upper case letter, ensure the next letter is lower case.
            foundFirstCapitalLetter = true;
          }
        } else if (foundFirstCapitalLetter === true) {
          if (gen.cLowerCaseEnglishAlphabet.includes(inputData.charAt(i))) {
            returnData = true;
            break; // Sufficient evidence to prove this is a camel case string.
          }
        }
      } // End-for (let i = 1; i < inputData.length; i++)
    } // End-if (!inputData.match(/[\s_-]/g) && doesStringContainUpperCaseCharacter(inputData, '') &&...)
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function mapWordToCamelCaseWord
 * @description Takes a string key and an indexed string value and maps the word to an upper case first letter word.
 * @param {string} inputData The string key/value that should be converted to a camel case word.
 * @param {string} inputMetaData The string index for the map to the value that should be used.
 * @return {string} A string where the word has been converted into a camel case word.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function mapWordToCamelCaseWord(inputData, inputMetaData) {
  let functionName = mapWordToCamelCaseWord.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = false;
  if (inputData) {
    returnData = inputData.replace(/^./, character => character.toUpperCase());
  }
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function simplifyAndConsolidateString
 * @description Takes a string, and returns a version of it converted to lower case,
 * with all digits and symbols and whitespace removed.
 * @param {string} inputData The string that should be simplified and consolidated.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {string} A string that has been simplified and consolidated by converting to lower case, removing all digits, symbols and white space.
 * @author Seth Hollingsead
 * @date 2022/01/23
 * @NOTE I think this function is not completely working as expected, probably something to do with that regular expression.
 * Input was: 11UpberDriver321CodeClearance0x#0000FF-akaBlue
 * Output was: upberdrivercodeclearanceffakablue
 */
async function simplifyAndConsolidateString(inputData, inputMetaData) {
  let functionName = simplifyAndConsolidateString.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = '';
  if (inputData) {
    // returnData = inputData.toLowerCase().replace(/[\W]/g, '');
    returnData = await ruleParsing.processRulesInternal([inputData.toLowerCase().trim(), [/[^\w\s]/g, '']], [biz.cutilitiesReplaceCharacterWithCharacter]);
    returnData = await ruleParsing.processRulesInternal([returnData, [/[\0-9]/g, '']], [biz.cutilitiesReplaceCharacterWithCharacter]);
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function compareSimplifiedAndConsolidatedStrings
 * @description Compares two strings by their simplified versions (see simplifyAndConsolidateString()).
 * @param {string} inputData The first string to be compared.
 * @param {string} inputMetaData The second string to be compared.
 * @return {boolean} A True or False value to indicate if the strings are virtually identical or not.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function compareSimplifiedAndConsolidatedStrings(inputData, inputMetaData) {
  let functionName = compareSimplifiedAndConsolidatedStrings.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = false;
  if (inputData && inputMetaData) {
    returnData = await simplifyAndConsolidateString(inputData, '') === await simplifyAndConsolidateString(inputMetaData, '');
  }
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function countCamelCaseWords
 * @description Takes a string in camelCase and returns the number of words that it contains based on camel case rules.
 * @param {string} inputData String to count words from.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {integer} The number of camel case words found in the string.
 * @author Seth Hollingsead
 * @date 2022/01/23
 * @NOTE Might not work so well with numbers as part of the string, they are not treated as capital letters.
 * We might need to do some refactoring of this function if
 * mixed numbers and camel case strings ever becomes a requirement as input to this function.
 * @NOTE Based on the implementation for the business rule/function arrayParsing.convertCamelCaseStringToArray.
 */
async function countCamelCaseWords(inputData, inputMetaData) {
  let functionName = countCamelCaseWords.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = 0;
  if (inputData) {
    let caps = [];
    for (let i = 0; i < inputData.length; i++) {
      await loggers.consoleLog(namespacePrefix + functionName, msg.ccharacterIs + inputData.charAt(i));
      if (gen.cUpperCaseEnglishAlphabet.includes(inputData.charAt(i))) {
        // Upper case letter found.
        await loggers.consoleLog(namespacePrefix + functionName, msg.cupperCaseLetterFound);
        caps.push(i);
      }
    } // End-for (let i = 1; i < inputData.length; i++)
    returnData = caps.length;
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function doesStringContainAcronym
 * @description Scans a string and determines if there are 2 or more immediately adjacent upper-case characters in the string.
 * Example: nodeJS where JS is an acronym for JavaScript.
 * @param {string} inputData The string that should be scanned to determine if it contains an acronym or not.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {boolean} True or False to indicate if the input string contains an acronym.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function doesStringContainAcronym(inputData, inputMetaData) {
  let functionName = doesStringContainAcronym.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = false;
  let lastCharacterWasUpperCase = false;
  if (inputData) {
    for (let i = 1; i < inputData.length; i++) {
      // if the last character was upper case and the current character is upper case,
      // then we have found an acronym and we can exit the loop.
      if (lastCharacterWasUpperCase === true && gen.cUpperCaseEnglishAlphabet.includes(inputData.charAt(i))) {
        returnData = true;
        break;
      }
      if (gen.cUpperCaseEnglishAlphabet.includes(inputData.charAt(i))) {
        lastCharacterWasUpperCase = true;
      } else {
        lastCharacterWasUpperCase = false;
      }
    } // End-for (let i = 1; i < inputData.length; i++)
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function determineWordDelimiter
 * @description Determines what delimiter should e used to break a string up into words if possible.
 * @param {string} inputData The string that should be examined to determine what delimiter should be used to break it up into words.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {string} The delimiter that should be used, or if camelCase then the function will return the string "CamelCase".
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function determineWordDelimiter(inputData, inputMetaData) {
  let functionName = determineWordDelimiter.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = '';
  if (inputData) {
    let camelCaseWordCount = await countCamelCaseWords(inputData, '');
    await loggers.consoleLog(namespacePrefix + functionName, msg.ccamelCaseWordCountIs + camelCaseWordCount);
    let containsAcronym = await doesStringContainAcronym(inputData, '');
    await loggers.consoleLog(namespacePrefix + functionName, msg.ccontainsAcronymIs + containsAcronym);
    let spacesCount = await countDelimiterInString(inputData, bas.cSpace);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cspacesCountIs + spacesCount);
    let periodCount = await countDelimiterInString(inputData, bas.cDot);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cperiodCountIs + periodCount);
    let dashCount = await countDelimiterInString(inputData, bas.cDash);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cdashCountIs + dashCount);
    let comaCount = await countDelimiterInString(inputData, bas.cComa);
    await loggers.consoleLog(namespacePrefix + functionName, msg.ccomaCountIs + comaCount);
    let underscoreCount = await countDelimiterInString(inputData, bas.cUnderscore);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cunderscoreCountIs + underscoreCount);
    let plusCount = await countDelimiterInString(inputData, bas.cPlus);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cplusCountIs + plusCount);
    let percentCount = await countDelimiterInString(inputData, bas.cPercent);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cpercentCountIs + percentCount);
    if (
    camelCaseWordCount > 0 &&
    containsAcronym === false &&
    spacesCount === 0 &&
    periodCount === 0 &&
    dashCount === 0 &&
    comaCount === 0 &&
    underscoreCount === 0 &&
    plusCount === 0 &&
    percentCount === 0) {
      returnData = sys.cCamelCase;
      // We haven't hit the case eyt where we need to differentiate between all these extra cases, and there are several of them.
      // We could have multiple acronyms in a word, or in multiple words that are camelCase.
      // Each of these could be really complex special cases.
      // If we get to that point we will handle those cases on a case by case basis to improve the algorithm.
    } else if (spacesCount > 0 && periodCount === 0 && dashCount === 0 && comaCount === 0 && underscoreCount === 0 && plusCount === 0 && percentCount === 0) {
      returnData = bas.cSpace;
    } else if (spacesCount === 0 && periodCount > 0 && dashCount === 0 && comaCount === 0 && underscoreCount === 0 && plusCount === 0 && percentCount === 0) {
      returnData = bas.cDot;
    } else if (spacesCount === 0 && periodCount === 0 && dashCount > 0 && comaCount === 0 && underscoreCount === 0 && plusCount === 0 && percentCount === 0) {
      returnData = bas.cDash;
    } else if (spacesCount === 0 && periodCount === 0 && dashCount === 0 && comaCount > 0 && underscoreCount === 0 && plusCount === 0 && percentCount === 0) {
      returnData = bas.cComa;
    } else if (spacesCount === 0 && periodCount === 0 && dashCount === 0 && comaCount === 0 && underscoreCount > 0 && plusCount === 0 && percentCount === 0) {
      returnData = bas.cUnderscore;
    } else if (spacesCount === 0 && periodCount === 0 && dashCount === 0 && comaCount === 0 && underscoreCount === 0 && plusCount > 0 && percentCount === 0) {
      returnData = bas.cPlus;
    } else if (spacesCount === 0 && periodCount === 0 && dashCount === 0 && comaCount === 0 && underscoreCount === 0 && plusCount === 0 && percentCount > 0) {
      returnData = bas.cPercent;
    } else {
      if (spacesCount > periodCount && spacesCount > dashCount && spacesCount > comaCount && spacesCount > underscoreCount && spacesCount > plusCount && spacesCount > percentCount) {
        returnData = bas.cSpace;
      } else if (periodCount > spacesCount && periodCount > dashCount && periodCount > comaCount && periodCount > underscoreCount && periodCount > plusCount && periodCount > percentCount) {
        returnData = bas.cDot;
      } else if (dashCount > spacesCount && dashCount > periodCount && dashCount > comaCount && dashCount > underscoreCount && dashCount > plusCount && dashCount > percentCount) {
        returnData = bas.cDash;
      } else if (comaCount > spacesCount && comaCount > periodCount && comaCount > dashCount && comaCount > underscoreCount && comaCount > plusCount && comaCount > percentCount) {
        returnData = bas.cComa;
      } else if (underscoreCount > spacesCount && underscoreCount > periodCount && underscoreCount > dashCount && underscoreCount > comaCount && underscoreCount > plusCount && underscoreCount > percentCount) {
        returnData = bas.cUnderscore;
      } else if (plusCount > spacesCount && plusCount > periodCount && plusCount > dashCount && plusCount > comaCount && plusCount > underscoreCount && plusCount > percentCount) {
        returnData = bas.cPlus;
      } else if (percentCount > spacesCount && percentCount > periodCount && percentCount > dashCount && percentCount > comaCount && percentCount > underscoreCount && percentCount > plusCount) {
        returnData = bas.cPercent;
      } else {
        // We don't need to be showing this warning unless we are debugging.
        // WARNING: Mixed string. Cannot determine what delimiter should be used to break up the string into words.
        await loggers.consoleLog(namespacePrefix + functionName, msg.cDetermineWordDelimiterMessage1 + msg.cDetermineWordDelimiterMessage2 +
          msg.cDetermineWordDelimiterMessage3 + msg.cDetermineWordDelimiterMessage4);
      }
    }
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function countDelimiterInString
 * @description Takes a string and returns the number of specified delimiters it contains.
 * @param {string} inputData String to count delimiters from.
 * @param {string} inputMetaData The delimiter that should be used when counting from the input string.
 * @return {integer} The number of delimiters found in the string.
 * @author Seth Hollingsead
 * @date 2022/01/23
 * @NOTE: https://stackoverflow.com/questions/35849174/count-spaces-in-a-string
 */
async function countDelimiterInString(inputData, inputMetaData) {
  let functionName = countDelimiterInString.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = 0;
  if (inputData && inputMetaData) {
    returnData = (inputData.split(inputMetaData).length - 1);
  }
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function getWordCountInString
 * @description Gets the number of words in a string, it expects words to be
 * delimited by either camel-case, spaces, period, dash, underscore, plus or percent symbols.
 * @param {string} inputData The string that words should be counted from.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {integer} The number of words that were found in the string.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function getWordCountInString(inputData, inputMetaData) {
  let functionName = getWordCountInString.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = 0;
  if (inputData) {
    let wordDelimiter = await determineWordDelimiter(inputData, inputMetaData);
    if (wordDelimiter === sys.cCamelCase) {
      returnData = await countCamelCaseWords(inputData, '');
    } else if (wordDelimiter !== '') {
      returnData = inputData.split(wordDelimiter).length;
    } else {
      // We don't need to be showing this warning unless we are debugging.
      // WARNING: Mixed string. Cannot determine how words are delimited in the string. Unable to count words.
      await loggers.consoleLog(namespacePrefix + functionName, msg.cGetWordCountInStringMessage1 + msg.cGetWordCountInStringMessage2 + msg.cGetWordCountInStringMessage3);
    }
  } // End-if (inputData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function isStringList
 * @description Determines if the input string is a list or not.
 * @param {string} inputData The string that should be checked if it is a list or not.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {boolean} True or False to indicate if the input string is a list or not a list.
 * Using the list of system defined primary, secondary or tertiary command delimiters.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function isStringList(inputData, inputMetaData) {
  let functionName = isStringList.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  console.log(msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  console.log(msg.cinputMetaDataIs + inputMetaData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = false;
  if (inputData) {
    let primaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.cprimaryCommandDelimiter);
    let secondaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.csecondaryCommandDelimiter);
    let tertiaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.ctertiaryCommandDelimiter);
    if (inputData.includes(primaryCommandDelimiter) === true ||
    inputData.includes(secondaryCommandDelimiter) === true ||
    inputData.includes(tertiaryCommandDelimiter) === true) {
      returnData = true;
    }
  } // End-if (inputData)
  console.log(msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

/**
 * @function aggregateNumericalDifferenceBetweenTwoStrings
 * @description Determines the delta difference between the two input strings and returns it as a number.
 * @param {string} inputData String 1 to compare.
 * @param {string} inputMetaData String 2 to compare.
 * @return {integer} The delta difference between the two strings, expressed as a number.
 * @author Seth Hollingsead
 * @date 2022/01/23
 */
async function aggregateNumericalDifferenceBetweenTwoStrings(inputData, inputMetaData) {
  let functionName = aggregateNumericalDifferenceBetweenTwoStrings.name;
  await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + inputData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
  let returnData = -1;
  if (inputData && inputMetaData) {
    // Convert the input strings to lower case and clean them up for parsing.
    let string1 = inputData.toLowerCase().replace(/\W/g, '');
    let string2 = inputMetaData.toLowerCase().replace(/\W/g, '');
    await loggers.consoleLog(namespacePrefix + functionName, msg.cstring1Is + string1);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cstring2Is + string2);

    // Build some arrays of variations on string 2, we will use these for doing the comparisons.
    let variation0 = Array(string2.length + 1).fill(0).map((v, i) => i);
    let variation1 = Array(string2.length + 1).fill(0);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cvariation0ValueIs + variation0);
    await loggers.consoleLog(namespacePrefix + functionName, msg.cvariation1ValueIs + variation1);

    for (let i = 0; i < string1.length; i++) {
      await loggers.consoleLog(namespacePrefix + functionName, msg.ciValueIs + i);
      variation1[0] = i + 1;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cvariation0ValueIs + variation0);
      for (let j = 0; j < string2.length; j++) {
        await loggers.consoleLog(namespacePrefix + functionName, msg.cjValueIs + j);
        let deletionCost = variation0[j + 1] + 1;
        let insertionCost = variation1[j] + 1;
        let substitutionCost;
        if (string1[i] === string2[j]) {
          substitutionCost = variation0[j];
        } else {
          substitutionCost = variation0[j] + 1;
        }
        await loggers.consoleLog(namespacePrefix + functionName, msg.cdeletionCostIs + deletionCost);
        await loggers.consoleLog(namespacePrefix + functionName, msg.cinsertionCostIs + insertionCost);
        await loggers.consoleLog(namespacePrefix + functionName, msg.csubstitutionCostIs + substitutionCost);
        variation1[j + 1] = math.min(deletionCost, insertionCost, substitutionCost);
        await loggers.consoleLog(namespacePrefix + functionName, msg.cvariation1ValueIs + variation1);
      } // End-for (let j = 0; j < string2.length; j++)
      let temp = variation1;
      variation1 = variation0;
      variation0 = temp;
    } // End-for (let i = 0; i < string1.length; i++)
    returnData = variation0[string2.length];
  } // End-if (inputData && inputMetaData)
  await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
  await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
  return returnData;
}

export default {
  isStringCamelCase,
  mapWordToCamelCaseWord,
  simplifyAndConsolidateString,
  compareSimplifiedAndConsolidatedStrings,
  countCamelCaseWords,
  doesStringContainAcronym,
  determineWordDelimiter,
  countDelimiterInString,
  getWordCountInString,
  isStringList,
  aggregateNumericalDifferenceBetweenTwoStrings
};
