# Source: controllers/chiefPlugin.js

    /**
     * @file chiefPlugin.js
     * @module chiefPlugin
     * @description Contains all of the functions to manage the loading, unloading, reloading,
     * registering, unregistering of plugins and plugin metaData.
     * @requires module:pluginBroker
     * @requires module:chiefConstant
     * @requires module:chiefTheme
     * @requires module:loggers
     * @requires module:stack
     * @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
     * @requires {@link https://www.npmjs.com/package/path|path}
     * @author Seth Hollingsead
     * @date 2022/09/02
     * @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
     */

    // Internal imports
    import pluginBroker from '../brokers/pluginBroker.js';
    import chiefConstant from './chiefConstant.js';
    import chiefTheme from './chiefTheme.js';
    import loggers from '../executrix/loggers.js'
    import stack from '../structures/stack.js'
    // External imports
    import hayConst from '@haystacks/constants';
    import path from 'path';

    const {bas, msg, sys, wrd} = hayConst;
    const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
    // framework.controllers.chiefPlugin.
    const namespacePrefix = wrd.cframework + bas.cDot + wrd.ccontrollers + bas.cDot + baseFileName + bas.cDot;

    /**
     * @function loadPluginRegistryData
     * @description Loads the plugin registry meta data which includes paths to plugins.
     * @param {string} pluginRegistryPath
     * @return {object} A JSON object loaded from the specified path with the plugin paths and plugin metaData.
     * @author Seth Hollingsead
     * @date 2022/09/13
     */
    async function loadPluginRegistryData(pluginRegistryPath) {
      let functionName = loadPluginRegistryData.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginRegistryPath
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginRegistryPathIs + pluginRegistryPath);
      let returnData = {};
      returnData = await pluginBroker.loadPluginRegistry(pluginRegistryPath);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function persistPluginRegistryToDataStructure
     * @description Saves the plugin registry data to the D-data structure.
     * @param {object} pluginRegistryData
     * @return {boolean} True or False to indicate if the persistense was successful or not.
     * @author Seth Hollingsead
     * @date 2022/09/13
     */
    async function persistPluginRegistryToDataStructure(pluginRegistryData) {
      let functionName = persistPluginRegistryToDataStructure.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginRegistryData is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginRegistryDataIs + JSON.stringify(pluginRegistryData));
      let returnData = false;
      returnData = await pluginBroker.storePluginRegistryInDataStructure(pluginRegistryData);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function listLoadedPlugins
     * @description This is a wrapper function for pluginBroker.listAllLoadedPlugins.
     * @return {array<string>} A list array of the names of the plugins that are currently loaded.
     * @author Seth Hollingsead
     * @date 2023/02/06
     */
    async function listLoadedPlugins() {
      let functionName = listLoadedPlugins.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = [];
      returnData = await pluginBroker.listAllLoadedPlugins();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function getAllPluginsInRegistry
     * @description This is a wrapper function for pluginBroker.listPluginsInRegistry.
     * @return {array<string>} A list array of the names of the plugins in the plugin registry.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function getAllPluginsInRegistry() {
      let functionName = getAllPluginsInRegistry.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = [];
      returnData = await pluginBroker.listPluginsInRegistry();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function getAllPluginsPathsInRegistry
     * @description This is a wrapper function for pluginBroker.listPluginsPathsInRegistry
     * The purpose is to get the array of paths for all of the plugins that must be loaded.
     * @return {array<string>} A list array of all the paths of all the plugins in the plugin registry.
     * @author Seth Hollingsead
     * @date 2022/09/20
     */
    async function getAllPluginsPathsInRegistry() {
      let functionName = getAllPluginsPathsInRegistry.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = [];
      returnData = await pluginBroker.listPluginsPathsInRegistry();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function getAllPluginsInRegistryPath
     * @description This is a wrapper function for pluginBroker.listPluginsInRegistryPath.
     * @return {array<string>} A list array of the names of the plugins located at the specified path on
     * the local system from the plugins registry data hive.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function getAllPluginsInRegistryPath() {
      let functionName = getAllPluginsInRegistryPath.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = [];
      returnData = await pluginBroker.listPluginsInRegistryPath()
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function countAllPluginsInRegistry
     * @description This is a wrapper function for pluginBroker.countPluginsInRegistry.
     * @return {integer} The count of the number of plugins listed in the plugin registry data hive.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function countAllPluginsInRegistry() {
      let functionName = countAllPluginsInRegistry.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = 0;
      returnData = await pluginBroker.countPluginsInRegistry();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function countAllPluginsInRegistryPath
     * @description This is a wrapper function for pluginBroker.countPluginsInRegistryPath.
     * @return {integer} The count of the number of plugin sub-folders in the plugins path listed in the plugin registry data hive.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function countAllPluginsInRegistryPath() {
      let functionName = countAllPluginsInRegistryPath.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = 0;
      returnData = await pluginBroker.countPluginsInRegistryPath();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function registerNamedPlugin
     * @description This is a wrapper function for pluginBroker.registerPlugin.
     * @param {string} pluginName The name of the plugin that should be registered.
     * @param {string} pluginPath The path to the plugin, to be added to the plugin registry.
     * This should be the path to the plugin/package.json file, but not including the package.json as part of the path URI.
     * @return {boolean} True or False to indicate if the plugin was added to the plugin registry successfully or not.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function registerNamedPlugin(pluginName, pluginPath) {
      let functionName = registerNamedPlugin.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginName is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
      // pluginPath is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginPathIs + pluginPath);
      let returnData = false;
      returnData = await pluginBroker.registerPlugin(pluginName, pluginPath);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function unregisterNamedPlugin
     * @description This is a wrapper function for pluginBroker.unregisterPlugin.
     * @param {string} pluginName The name of the plugin that should be removed from the plugin registry.
     * @return {boolean} True or False to indicate if the plugin was removed from the plugin registry successfully or not.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function unregisterNamedPlugin(pluginName) {
      let functionName = unregisterNamedPlugin.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginName is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
      let returnData = false;
      returnData = await pluginBroker.unregisterPlugin(pluginName);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function unregisterPlugins
     * @description This is a wrapper function for pluginBroker.unregisterPlugins.
     * @param {array<string>} pluginListArray A list array of plugin names that should be removed from the plugin registry.
     * @return {boolean} True or False to indicate if all the plugins were removed from the plugin registry successfully or not.
     * @author Seth Hollingsead
     * @date 2023/02/07
     */
    async function unregisterPlugins(pluginListArray) {
      let functionName = unregisterPlugins.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginListArray is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginListArrayIs + JSON.stringify(pluginListArray));
      let returnData = false;
      returnData = await pluginBroker.unregisterPlugins(pluginListArray);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function synchronizePluginRegistryWithPath
     * @description This is a wrapper function for pluginBroker.syncPluginRegistryWithPluginRegistryPath.
     * @return {boolean} True or False to indicate if the synchronization was performed successfully or not.
     * @author Seth Hollingsead
     * @date 2022/09/15
     * @NOTE It is expected that the number of plugins loaded at any one time will not be crazy high.
     * The function that is called in the pluginBroker.syncPluginRegistryWithPluginRegistryPath will
     * do a O(n^2) brute force search,
     * if the number of plugins needed at any one time ever grows much over 100, then this solution will need to be re-evaluated!
     */
    async function synchronizePluginRegistryWithPath() {
      let functionName = synchronizePluginRegistryWithPath.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = false;
      returnData = await pluginBroker.syncPluginRegistryWithPluginRegistryPath();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function clearPluginRegistry
     * @description This is a wrapper function for pluginBroker.unregisterAllPlugins.
     * @return {boolean} True or False to indicate if the plugin registry data hive was cleared successfully or not.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function clearPluginRegistry() {
      let functionName = clearPluginRegistry.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = false;
      returnData = await pluginBroker.unregisterAllPlugins();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function savePluginRegistryDisk
     * @description This is a wrapper function for pluginBroker.savePluginRegistry.
     * @return {boolean} True or False to indicate if the plugin registry was successfully saved to the disk or not.
     * @author Seth Hollingsead
     * @date 2022/09/15
     */
    async function savePluginRegistryDisk() {
      let functionName = savePluginRegistryDisk.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = false;
      returnData = await pluginBroker.savePluginRegistry();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function loadAllPluginsMetaData
     * @description Loads the plugin meta data for all of the plugins in the input plugins path array.
     * @param {array<string>} pluginsPaths Should be the entry-point to the plugin,
     * but it could also be the name of the folder in the plugin registry path that is the entry-point to the plugin.
     * If its the later than we will need to compose the fully qualified path.
     * @return {array<object>} An array that contains all of the meta-data,
     * for each of the plugins in the input pluginsPaths array.
     * @author Seth Hollingsead
     * @date 2022/09/02
     */
    async function loadAllPluginsMetaData(pluginsPaths) {
      let functionName = loadAllPluginsMetaData.name
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginPaths are:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginsPathsAre + JSON.stringify(pluginsPaths));
      let returnData = [];
      let index = 0;
      if (pluginsPaths && pluginsPaths.length > 0) {
        for (let pluginKey in pluginsPaths) {
          // pluginKey is:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginKeyIs + pluginKey);
          let pluginPath = pluginsPaths[pluginKey];
          // pluginPath is:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginPathIs + pluginPath);
          returnData[index] = {};
          if (pluginPath != '') {
            returnData[index] = await pluginBroker.loadPluginMetaData(pluginPath);
          } // End-if (pluginPath != '')
          // loadedPluginMetaData at index:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cloadedPluginMetaDataAtIndex +
            // data:
            index + msg.cSpaceDataColonSpace + JSON.stringify(returnData[index]));
          index = index + 1;
        } // End-for (let pluginPath in pluginsPaths)
      } else {
        // ERROR: No plugin paths provided, please provide paths and try again.
        let errorMessage = msg.cErrorLoadAllPluginsMetaDataMessage01;
        console.log(errorMessage); // Make sure to output the error!
      }
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function loadAllPluginsExecutionPaths
     * @description Extracts the path to the main entry point for each plugin and does a path join to form a
     * fully qualified path, then converts the fully qualified path to a file URI so it can be imported.
     * @param {array<object>} pluginsMetaData An array of JSON objects that contain all of the package.json data
     * loaded for each plugin that should be imported.
     * @param {array<string>} pluginsPaths An array of fully qualified paths where each plugin was loaded from,
     * used to path.join with the execution path entry point file for each plugin.
     * @return {array<string>} An array of fully qualified path URI's for each of the main entry points for
     * all of the plugins that should be loaded.
     * @author Seth Hollingsead
     * @date 2022/09/02
     */
    async function loadAllPluginsExecutionPaths(pluginsMetaData, pluginsPaths) {
      let functionName = loadAllPluginsExecutionPaths.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginsMetaData is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginsMetaDataIs + JSON.stringify(pluginsMetaData));
      // pluginPaths are:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginsPathsAre + JSON.stringify(pluginsPaths));
      let returnData = [];
      let index = 0;
      if (pluginsMetaData && pluginsMetaData.length > 0) {
        for (let pluginMetaDataKey in pluginsMetaData) {
          // pluginMetaDataKey is:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginMetaDataKeyIs + pluginMetaDataKey);
          let pluginMetaData = pluginsMetaData[pluginMetaDataKey];
          // pluginMetaData is:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginMetaDataIs + JSON.stringify(pluginMetaData));
          returnData[index] = '';
          if (pluginMetaData) {
            returnData[index] = await pluginBroker.extractAndProcessPluginEntryPointURI(pluginMetaData, pluginsPaths[index]);
            // pluginPathURI at index:
            await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginPathUriAtIndex + index + bas.cSpace + wrd.cData + bas.cColon + bas.cSpace + returnData[index]);
          }
          index = index + 1;
        } // End-for (let pluginMetaData in pluginsMetaData)
      } else {
        // ERROR: No plugin meta data was found. Please ensure the correct path is provided and try again.
        let errorMessage = msg.cErrorLoadAllPluginsExecutionPathsMessage01;
        console.log(errorMessage); // Make sure to output the error!
      }
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function loadAllPlugins
     * @description This is a wrapper function that iterates over the list of plugin URI paths to import and calls the
     * loadPlugin function on the pluginBroker to actually load the plugin data, metaData, business rules, commands,
     * constants, constants validation, command aliases, configuration settings, debug settings and workflows.
     * @param {array<string>} pluginsExecutionPaths An array of fully qualified path URI's to be imported and executed for each plugin.
     * @param {array<object>} pluginsMetaData An array of JSON objects that contain all of the package.json data
     * loaded for each plugin that should be imported, we are passing this along here,
     * to have access to the name of each plugin as it is loaded.
     * @return {object} An object containing objects that contain all of the data and functions from each plugin,
     * these will need to be decomposed and merged with the D-data structure as appropriate.
     * @author Seth Hollingsead
     * @date 2022/09/01
     */
    async function loadAllPlugins(pluginsExecutionPaths, pluginsMetaData) {
      let functionName = loadAllPlugins.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginsExecutionPaths is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginsExecutionPathsIs + JSON.stringify(pluginsExecutionPaths));
      // pluginsMetaData is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginsMetaDataIs + JSON.stringify(pluginsMetaData));
      let returnData = {};
      let index = 0;
      if (pluginsExecutionPaths && pluginsMetaData && pluginsExecutionPaths.length > 0 && pluginsMetaData.length > 0) {
        await stack.initStack(sys.cpluginsLoaded);
        for (let pluginExecutionPathKey in pluginsExecutionPaths) {
          let pluginExecutionPath = pluginsExecutionPaths[pluginExecutionPathKey];
          let pluginMetaData = pluginsMetaData[index];
          if (pluginExecutionPath && pluginMetaData) {
            // pluginExecutionPath is:
            await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginExecutionPathIs + pluginExecutionPath);
            // pluginMetaData is:
            await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginMetaDataIs + JSON.stringify(pluginMetaData));
            returnData[pluginMetaData[wrd.cname]] = {}; // Initialize the data structure
            // Load the data and add it.
            try {
              let pluginReturnedData = await pluginBroker.loadPlugin(pluginExecutionPath);
              if (pluginReturnedData) {
                returnData[pluginMetaData[wrd.cname]] = pluginReturnedData
                // loaded plugin:
                console.log(msg.cloadedPlugin + pluginMetaData[wrd.cname]);
              } else {
                // There was an error attempting to load the plugin:
                console.log(msg.cErrorLoadingPlugin + pluginExecutionPath);
              }
            } catch (err) {
              // Failed to load the plugin:
              console.log(msg.cERROR_Colon + namespacePrefix + functionName + msg.cloadAllPluginsMessage01 + pluginMetaData[wrd.cname]);
              // plugin entry point path:
              console.log(msg.cloadAllPluginsMessage02 + pluginExecutionPath);
              console.log(msg.cERROR_Colon + err);
              stack.push(sys.cpluginsLoaded, [pluginMetaData[wrd.cname], false]);
            }
            // Push to a stack that we have successfully loaded the currently named plugin,
            // so that all the plugins loaded can be verified.
            stack.push(sys.cpluginsLoaded, [pluginMetaData[wrd.cname], true]);
          } else {
            // Failed to load the plugin:
            console.log(msg.cERROR_Colon + namespacePrefix + functionName + msg.cloadAllPluginsMessage01 + pluginMetaData[wrd.cname]);
            // plugin entry point path:
            console.log(msg.cloadAllPluginsMessage02 + pluginExecutionPath);
            stack.push(sys.cpluginsLoaded, [pluginMetaData[wrd.cname], false]);
          }
          index = index + 1;
        } // End-for (let pluginExecutionPath in pluginsExecutionPaths)
      } else {
        // ERROR: No plugin execution paths or plugins metaData was specified:
        console.log(msg.cloadAllPluginsMessage03 + namespacePrefix + functionName);
      }
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function integrateAllPluginsData
     * @description Integrates all plugin data for each plugin with the haystacks data structures,
     * this is the final step if the loading of the plugins and basically ensures that all runtime operations of all of the loaded plugins
     * are fully integrated and activated within the haystacks execution environment.
     * @param {object} allPluginsData All of the plugins data for each plugin all combined together into a single JSON data object.
     * @return {boolean} True or False to indicate if all of the data integration was successful or not.
     * @author Seth Hollingsead
     * @date 2022/10/23
     */
    async function integrateAllPluginsData(allPluginsData) {
      let functionName = integrateAllPluginsData.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // allPluginsData is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.callPluginsDataIs + JSON.stringify(allPluginsData));
      let returnData = true;
      if (allPluginsData) {
        for (const key in allPluginsData) {
          let pluginName = key;
          // pluginName is:
          await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
          let dataIntegrationSuccess = await integratePluginData(pluginName, allPluginsData[pluginName]);
          if (dataIntegrationSuccess === false) {
            returnData = dataIntegrationSuccess;
          }
        } // End-for (const key in allPluginsData)
      } else {
        // ERROR: Invalid data input, unable to integrate all plugin data.
        console.log(msg.cErrorIntegrateAllPluginsDataMessage01);
      }
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function integratePluginData
     * @description Integrates a single plugin data set with the haystacks data structures.
     * @param {string} pluginName The name of the plugin that should have its data integrated with the haystacks data structure.
     * @param {object} pluginData The data for the named plugin that should be integrated wtih the haystacks data structure.
     * @return {boolean} True or False to indicate if all the plugin data was integrated successfully with the haystacks data structures.
     * @author Seth Hollingsead
     * @date 2022/10/23
     */
    async function integratePluginData(pluginName, pluginData) {
      let functionName = integratePluginData.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginName is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
      // pluginData is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginDataIs + JSON.stringify(pluginData));
      let returnData = false;
      let businessRulesIntegrationResult = false;
      let commandsIntegrationResult = false;
      let configurationDataIntegrationResult = false;
      let commandAliasesIntegrationResult = false;
      let workflowsIntegrationResult = false;
      let constantsValidationDataIntegrationResult = false;
      let themeDataIntegrationResult = false;
      if (pluginData && pluginName) {
        if (pluginData[wrd.cdata] !== undefined) {
          if (pluginData[wrd.cdata][sys.cpluginBusinessRules] !== undefined) {
            businessRulesIntegrationResult = await pluginBroker.integratePluginBusinessRules(pluginName, pluginData[wrd.cdata][sys.cpluginBusinessRules]);
          } else {
            // ERROR: No plugin business rules data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage02 + pluginName);
          }
          if (pluginData[wrd.cdata][sys.cpluginCommands] !== undefined) {
            commandsIntegrationResult = await pluginBroker.integratePluginCommands(pluginName, pluginData[wrd.cdata][sys.cpluginCommands]);
          } else {
            // ERROR: No plugin commands data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage03 + pluginName);
          }
          if (pluginData[wrd.cdata][wrd.cconfiguration] !== undefined) {
            configurationDataIntegrationResult = await pluginBroker.integratePluginConfigurationData(pluginName, pluginData[wrd.cdata][wrd.cconfiguration]);
          } else {
            // ERROR: No plugin configuration data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage04 + pluginName);
          }
          if (pluginData[wrd.cdata][sys.cCommandsAliases] !== undefined) {
            commandAliasesIntegrationResult = await pluginBroker.integratePluginCommandAliases(pluginName, pluginData[wrd.cdata][sys.cCommandsAliases]);
          } else {
            // ERROR: No plugin command aliases data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage05 + pluginName);
          }
          if (pluginData[wrd.cdata][sys.cCommandWorkflows] !== undefined) {
            workflowsIntegrationResult = await pluginBroker.integratePluginWorkflows(pluginName, pluginData[wrd.cdata][sys.cCommandWorkflows]);
          } else {
            // ERROR: No plugin workflows data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage06 + pluginName);
          }
          if (pluginData[wrd.cdata][sys.cpluginConstantsValidationData] !== undefined) {
            constantsValidationDataIntegrationResult = await chiefConstant.addConstantsValidationData(pluginData[wrd.cdata][sys.cpluginConstantsValidationData],
              wrd.cPlugin + bas.cColon + pluginName);
          } else {
            // ERROR: No plugin constants validation data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage07 + pluginName);
          }
          if (pluginData[wrd.cdata][wrd.cThemes] !== undefined) {
            themeDataIntegrationResult = await chiefTheme.addThemeData(pluginData[wrd.cdata][wrd.cThemes], wrd.cPlugin + bas.cColon + pluginName);
          } else {
            // ERROR: No plugin themes data was loaded for the plugin:
            console.log(msg.cErrorIntegratePluginDataMessage08 + pluginName);
          }
        } else {
          // ERROR: No plugin data was loaded at all for the plugin:
          console.log(msg.cErrorIntegratePluginDataMessage09 + pluginName);
        }
      } else {
        // ERROR: Invalid input, either the plugin name or plugin data was undefined. Please provide valid data and try again.
        console.log(msg.cErrorIntegratePluginDataMessage01);
        // pluginName is:
        console.log(msg.cpluginNameIs + pluginName);
        // pluginData is:
        console.log(msg.cpluginDataIs + JSON.stringify(pluginData));
      }
      if (businessRulesIntegrationResult === true &&
        commandsIntegrationResult === true &&
        configurationDataIntegrationResult === true &&
        commandAliasesIntegrationResult === true &&
        workflowsIntegrationResult === true &&
        constantsValidationDataIntegrationResult === true &&
        themeDataIntegrationResult === true) {
          returnData = true;
        }
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function verifyAllPluginsLoaded
     * @description Examines the pluginsLoaded stack to confirm that none of the plugins which
     * were supposed to have been loaded failed to load.
     * @return {boolean} True or False to indicate if all of the plugins were loaded successfully or not.
     * @author Seth Hollingsead
     * @date 2022/09/06
     */
     async function verifyAllPluginsLoaded() {
      let functionName = verifyAllPluginsLoaded.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = false;
      let foundFailedPlugin = false;
      let stackLength = stack.length(sys.cpluginsLoaded);
      for (let i = 0; i <= stackLength; i++) {
        let pluginLoadedObject = stack.pop(sys.cpluginsLoaded);
        if (Array.isArray(pluginLoadedObject)) {
          if (pluginLoadedObject[1] === false) {
            foundFailedPlugin = true;
          }
        } // End-if (Array.isArray(pluginLoadedObject))
      } // End-for (let i = 0; i <= stackLength; i++)
      returnData = !foundFailedPlugin; // Invert the logic
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function unloadPlugin
     * @description Unloads a plugin by removing all of the plugin data and meta-data from all of the
     * appropriate data structures in the D-data structure.
     * This is a wrapper function for calling the pluginBroker to get the work done.
     * @param {string} pluginName The name of the plugin that should have all its data unloaded from the D-data structure.
     * @return {boolean} True or False to indicate if the plugin was unloaded successfully or not.
     * @author Seth Hollingsead
     * @date 2023/02/01
     */
    async function unloadPlugin(pluginName) {
      let functionName = unloadPlugin.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // pluginName is:
      await loggers.consoleLog(namespacePrefix + functionName, msg.cpluginNameIs + pluginName);
      let returnData = false;
      returnData = await pluginBroker.unloadPlugin(pluginName);
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData);
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
     * @function getPluginsRegistryPath
     * @description This is a wrapper function for the pluginBroker.getPluginsRegistryPath.
     * @return {string} The path to the plugins listed in the plugin registry as meta-data.
     * @author Seth Hollingsead
     * @date 2023/02/07
     */
    async function getPluginsRegistryPath() {
      let functionName = getPluginsRegistryPath.name;
      await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = '';
      returnData = await pluginBroker.getPluginsRegistryPath();
      await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    export default {
      loadPluginRegistryData,
      persistPluginRegistryToDataStructure,
      listLoadedPlugins,
      getAllPluginsInRegistry,
      getAllPluginsPathsInRegistry,
      getAllPluginsInRegistryPath,
      countAllPluginsInRegistry,
      countAllPluginsInRegistryPath,
      registerNamedPlugin,
      unregisterNamedPlugin,
      unregisterPlugins,
      synchronizePluginRegistryWithPath,
      clearPluginRegistry,
      savePluginRegistryDisk,
      loadAllPluginsMetaData,
      loadAllPluginsExecutionPaths,
      loadAllPlugins,
      integrateAllPluginsData,
      integratePluginData,
      verifyAllPluginsLoaded,
      unloadPlugin,
      getPluginsRegistryPath
    };