importPackage(Packages.de.elo.ix.client);

//@include lib_Class.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.RepoUtils.js
//@include lib_sol.common.Template.js
//@include lib_sol.common.TranslateTerms.js
//@include lib_sol.common.ix.ServiceBase.js

var logger = sol.create("sol.Logger", { scope: "sol.common.ix.services.ActionDefinitionCollector" });

var _0x9311=["\x64\x65\x73\x63","\x70\x61\x72\x73\x65","\x63\x6C\x69\x65\x6E\x74\x41\x70\x70\x54\x79\x70\x65","\x73\x65\x73\x73\x69\x6F\x6E\x4F\x70\x74\x69\x6F\x6E\x73","\x44\x52\x46\x4A","\x6C\x61\x75\x6E\x63\x68\x70\x61\x64","\x63\x6C","\x65","\x45\x4C\x4F\x4D\x69\x6E\x69\x41\x70\x70\x2F","\x6C\x69\x63","\x58","\x69\x6E\x64\x65\x78\x4F\x66","\x66\x63\x74","\x61\x63\x74\x69\x6F\x6E","\x2F","\x6E\x61\x6D\x65","\x62\x75\x74\x74\x6F\x6E","\x72\x69\x62\x62\x6F\x6E","\x73\x75\x62\x73\x74\x72\x69\x6E\x67","\x45\x4C\x4F\x62\x73","\x73\x65\x74\x43\x75\x73\x74\x6F\x6D\x65\x72\x4E\x61\x6D\x65","\x73\x65\x74\x4D\x6F\x64\x75\x6C\x65\x4E\x61\x6D\x65","\x73\x65\x74\x4C\x69\x63\x65\x6E\x73\x65\x4B\x65\x79","\x63\x68\x65\x63\x6B\x4C\x69\x63\x65\x6E\x73\x65","\x69\x78","\x64\x63","","\x23","\x69\x64","\x75\x73\x65\x72","\x73\x69\x67","\x55\x54\x46\x2D\x38","\x67\x65\x74\x4D\x44\x35","\x4D\x64\x35\x48\x61\x73\x68","\x73\x65\x63","\x75\x74\x69\x6C\x73","\x65\x6C\x6F","\x64\x65","\x73\x65\x72\x6E\x6F","\x6C\x69\x63\x65\x6E\x73\x65","\x67\x65\x74\x53\x65\x72\x76\x65\x72\x49\x6E\x66\x6F"];var ActionDefinitionManager={register:function(_0x938fx2,_0x938fx3,_0x938fx4){var _0x938fx5=this,_0x938fx6;_0x938fx6= JSON[_0x9311[1]](_0x938fx2[_0x9311[0]]);return (_0x938fx3[_0x9311[3]][_0x9311[2]]== _0x9311[4]|| _0x938fx4== _0x9311[5])?((_0x938fx5[_0x9311[6]](_0x938fx6))?_0x938fx5[_0x9311[7]](_0x938fx3,_0x938fx6,true):null):_0x938fx5[_0x9311[7]](_0x938fx3,_0x938fx6,false)},cl:function(_0x938fx6){var _0x938fx5=this,_0x938fx7=false,_0x938fx8=_0x9311[8],_0x938fx9,_0x938fxa,_0x938fxb;if(_0x938fx6[_0x9311[9]]){_0x938fx9=  new LicenseInfo();if(_0x938fx6[_0x9311[9]][_0x9311[11]](_0x9311[10])=== 0){_0x938fx8+= _0x938fx6[_0x9311[13]][_0x9311[12]]+ _0x9311[14]+ _0x938fx6[_0x9311[17]][_0x9311[16]][_0x9311[15]];_0x938fxa= _0x938fx6[_0x9311[9]][_0x9311[18]](1)}else {_0x938fx8+= _0x938fx6[_0x9311[13]][_0x9311[12]];_0x938fxa= _0x938fx6[_0x9311[9]]};try{_0x938fx9[_0x9311[20]](_0x9311[19]);_0x938fx9[_0x9311[21]](_0x938fx8);_0x938fx9[_0x9311[22]](_0x938fxa);ixConnect[_0x9311[24]]()[_0x9311[23]](_0x938fx9);_0x938fx7= true}catch(ex){};if(_0x938fx7!== true){_0x938fxb= _0x938fx5[_0x9311[25]]();if(_0x938fxb){try{_0x938fx9[_0x9311[20]](_0x938fxb);_0x938fx9[_0x9311[21]](_0x938fx8);_0x938fx9[_0x9311[22]](_0x938fxa);ixConnect[_0x9311[24]]()[_0x9311[23]](_0x938fx9);_0x938fx7= true}catch(ex){}}}};return _0x938fx7},e:function(_0x938fx3,_0x938fx6,_0x938fxc){var _0x938fx8,_0x938fxd;_0x938fx8= _0x938fx6[_0x9311[13]][_0x9311[12]]+ _0x9311[14]+ ((_0x938fx6[_0x9311[17]]&& _0x938fx6[_0x9311[17]][_0x9311[16]])?_0x938fx6[_0x9311[17]][_0x9311[16]][_0x9311[15]]:_0x9311[26]);_0x938fxd= (_0x938fxc&& _0x938fx6[_0x9311[9]])?(_0x938fx6[_0x9311[9]]+ _0x9311[27]+ _0x938fx8+ _0x9311[27]+ _0x938fx3[_0x9311[29]][_0x9311[28]]):_0x938fx6[_0x9311[13]][_0x9311[12]];_0x938fx6[_0x9311[30]]= String(Packages[_0x9311[37]][_0x9311[36]][_0x9311[35]][_0x9311[34]][_0x9311[33]][_0x9311[32]](_0x938fxd,_0x9311[31]));return _0x938fx6},dc:function(){var _0x938fxb;try{_0x938fxb= ixConnect[_0x9311[24]]()[_0x9311[40]]()[_0x9311[39]][_0x9311[38]]}catch(ex){_0x938fxb= null};return _0x938fxb}} // eslint-disable-line

/**
 * Collects all ribbon and action definitions for ELO Business Solutions.
 * This service is usually called automatically during the client startup procedure.
 *
 * @author PZ, ELO Digital Office GmbH
 * @version 1.09.000
 */
sol.define("sol.common.ix.services.ActionDefinitionCollector", {
  extend: "sol.common.ix.ServiceBase",

  collectorVersion: "1.09.000",

  requiredClientVersions: {
    WCLB: { // JavaClient
      REPLACE: "20.00.000",
      MULTI: "30.00.000"
    },
    QRDC: { // WebClient
      REPLACE: "20.00.000",
      MULTI: "30.00.000"
    }
  },

  /*
   * Definition of the different modes:
   * LEGACY : mode for clients prior to ELO20
   * REPLACE: mode for ELO20 clients
   * MULTI  : mode for clients after ELO20
   */
  buttonMergeModes: {
    LEGACY: { name: "LEGACY", fct: "legacyMergeFct" },
    REPLACE: { name: "REPLACE", fct: "replaceMergeFct" },
    MULTI: { name: "MULTI", fct: "multiMergeFct" }
  },

  /**
   * @cfg {String} parentId (required)
   * id of the parent element (guid, objId or archivepath)
   */
  parentId: "ARCPATH[(E10E1000-E100-E100-E100-E10E10E10E00)]:/Business Solutions/_global/Action definitions",
  sordKeys: ["id", "guid", "name", "desc"],
  totalCount: 10000,
  maxFind: 1000,
  objKeys: [],
  filter: [],
  endLevel: 1,

  sordKeyMap: {
    id: { elementSelector: ObjDataC.mbId },
    guid: { elementSelector: ObjDataC.mbGuid },
    name: { elementSelector: ObjDataC.mbName },
    desc: { elementSelector: SordC.mbDesc }
  },

  initialize: function (config) {
    var me = this;

    me.$super("sol.common.ix.ServiceBase", "initialize", [config]);

    me.maskName = config.maskName || me.maskName;
    me.parentId = config.parentId || me.parentId;
    me.formatter = config.formatter || me.formatter;
    me.sordKeys = config.sordKeys || me.sordKeys;
    me.objKeys = config.objKeys || me.objKeys;
    me.ec = config.ec || me.ec;
    me.endLevel = config.endLevel || me.endLevel;
    me.returnDataDefinition = config.returnDataDefinition || me.returnDataDefinition;
    me.filter = config.filter || me.filter;

    me.objKeyMap = {};
    me.jsonData = [];
    me.docMasks = {};
  },

  /**
   * list of objects that are translated using IX translation keys.
   */
  translateObjectProperties: {
    ribbon: {
      button: {
        text: true,
        splitText: true,
        tooltipText: true,
        tooltipTitle: true
      },
      buttongroup: {
        text: true
      },
      ribbonTab: {
        text: true
      }
    },
    ribbons: {
      button: {
        text: true,
        splitText: true,
        tooltipText: true,
        tooltipTitle: true
      },
      buttongroup: {
        text: true
      },
      ribbonTab: {
        text: true
      }
    },
    action: {
      locale: {
        errorDlgTitle: true,
        typesDlgTitle: true,
        typesDlgHeader: true,
        typesDlgText: true,
        typesDlgNoTypes: true,
        treeDlgTitle: true,
        treeDlgHeader: true,
        treeDlgRootName: true
      }
    }
  },

  /**
   * Starts the collection of the desired data
   * @return {String}
   */
  execute: function () {
    var me = this,
        childSords, i, def, lang,
        terms = [],
        definitions = [];

    me.computeSordElementSelector();
    childSords = me.collectChildren();

    if (childSords) {
      // read definitions
      for (i = 0; i < childSords.length; i++) {
        try {
          def = ActionDefinitionManager.register(childSords[i], me.ec, me.client);

          def = me.transformRibbonDefinitions(def);

          if (def && me.checkRequiredModules(def)) {
            me.parseDefinition(me.translateObjectProperties, def, function (key, val) {
              terms.push(val);
              return null;
            });

            definitions.push(def);
          }
        } catch (ex) {
          me.logger.error("failed loading definition: " + childSords[i].name, ex);
        }
      }

      // translate definitions
      lang = String(me.ec.ci.language);
      sol.common.TranslateTerms.require(terms, lang);

      for (i = 0; i < definitions.length; i++) {
        def = definitions[i];
        me.parseDefinition(me.translateObjectProperties, def, function (key, val) {
          return sol.common.TranslateTerms.translate(val);
        });
      }
    }

    return JSON.stringify({
      definitions: definitions
    });
  },

  /**
   * @private
   * Helper for handling object structures.
   * @param {Object} base
   * @param {Object} obj
   * @param {Function} fct
   */
  parseDefinition: function (base, obj, fct) {
    var me = this,
        objName, res, arr, i;
    for (objName in base) {
      if (base && base.hasOwnProperty(objName)
          && obj && obj.hasOwnProperty(objName)) {
        if (base[objName] === true) {
          res = fct.call(me, objName, obj[objName], obj);
          if (res !== null && res !== "") {
            obj[objName] = res;
          }
        } else if (Array.isArray(obj[objName])) {
            arr = obj[objName];
            for (i = 0; i < arr.length; i++) {
              me.parseDefinition(base[objName], arr[i], fct);
            }
        } else {
          me.parseDefinition(base[objName], obj[objName], fct);
        }
      }
    }
  },

  /**
   * @private
   * @return {Array}
   */
  collectChildren: function () {
    var me = this,
        i, filter, idx, objKey, length,
        jsonData = [],
        findInfo = new FindInfo(),
        objKeyFilters = [],
        findResult,
        parentEditInfo, parentId;

    me.logger.enter("collectChildren");

    // get guid from arcpath
    // this allows accessing child elements of the given path without the requiremt of accessing
    // parent elements in the Administration folder.
    try {
      parentEditInfo = ixConnectAdmin.ix().checkoutSord(me.parentId, EditInfoC.mbOnlyId, LockC.NO);
      parentId = parentEditInfo.sord.id;
    } catch (exc) {
      me.logger.warn("'_global/Action definition' folder is missing.");
      return null;
    }

    findInfo.findChildren = new FindChildren();
    findInfo.findChildren.parentId = parentId;
    findInfo.findChildren.endLevel = me.endLevel;

    // apply optional mask filter
    if (me.maskName) {
      if (!findInfo.findByIndex) {
        findInfo.findByIndex = new FindByIndex();
      }
      findInfo.findByIndex.maskId = me.maskName;
    }

    // apply filter
    if (me.filter && me.filter.length > 0) {
      if (!findInfo.findByIndex) {
        findInfo.findByIndex = new FindByIndex();
      }
      for (i = 0; i < me.filter.length; i++) {
        filter = me.filter[i];
        objKey = new ObjKey();
        objKey.name = filter.key;
        objKey.data = [filter.val];
        objKeyFilters.push(objKey);
        me.logger.debug("applied filter:", objKey);
      }
      findInfo.findByIndex.objKeys = objKeyFilters;
    }

    // apply find options
    findInfo.findOptions = new FindOptions();
    findInfo.findOptions.totalCount = me.totalCount;

    try {
      idx = 0;
      findResult = ixConnect.ix().findFirstSords(findInfo, me.maxFind, me.sordZ);
      while (true) {
        for (i = 0, length = findResult.sords.length; i < length; i++) {
          jsonData.push(findResult.sords[i]);
        }
        if (!findResult.moreResults) {
          break;
        }
        idx += findResult.sords.length;
        findResult = ixConnect.ix().findNextSords(findResult.searchId, idx, me.maxFind, me.sordZ);
      }
    } finally {
      if (findResult) {
        ixConnect.ix().findClose(findResult.searchId);
      }
    }

    me.logger.exit("collectChildren");

    return jsonData;
  },

  /**
   * @private
   *
   * Computes a SordZ selector for information that is required.
   */
  computeSordElementSelector: function () {
    var me = this,
        sordKey, elementSelector, i, objKeyName;
    me.sordZ = new SordZ(ObjDataC.mbMask);
    if (me.sordKeys) {
      me.sordKeys.forEach(function (key) {
        sordKey = me.sordKeyMap[key];
        if (sordKey) {
          elementSelector = sordKey.elementSelector;
        }
        if (elementSelector) {
          me.sordZ.add(elementSelector);
        }
      });
    }
    if (me.objKeys) {
      me.sordZ.add(SordC.mbObjKeys);
      for (i = 0; i < me.objKeys.length; i++) {
        objKeyName = me.objKeys[i];
        me.objKeyMap[objKeyName] = true;
      }
    }
  },

  /**
   * @private
   * Determines the mode for mergeing the button and ribbon definitions.
   * @return {Object}
   */
  getButtonMergeMode: function () {
    var me = this,
        requiredVersions;

    requiredVersions = me.requiredClientVersions[me.ec.sessionOptions.clientAppType];

    if (requiredVersions && sol.common.RepoUtils.checkVersion(me.ec.sessionOptions.clientAppVersion, requiredVersions[me.buttonMergeModes.MULTI.name])) {
      return me.buttonMergeModes.MULTI;
    } else if (requiredVersions && sol.common.RepoUtils.checkVersion(me.ec.sessionOptions.clientAppVersion, requiredVersions[me.buttonMergeModes.REPLACE.name])) {
      return me.buttonMergeModes.REPLACE;
    }

    return me.buttonMergeModes.LEGACY;
  },

  transformRibbonDefinitions: function (actionDefinition) {
    var me = this,
        mergeMode, mergeFct;

    mergeMode = me.getButtonMergeMode();
    mergeFct = (mergeMode && mergeMode.fct) ? me[mergeMode.fct] : null;

    if (sol.common.ObjectUtils.isFunction(mergeFct)) {
      actionDefinition = mergeFct.call(me, actionDefinition);
    }

    return actionDefinition;
  },

  /**
   * @private
   * Checks, if all required modules are installed.
   * @param {Object} actionDefinition
   * @return {Boolean}
   */
  checkRequiredModules: function (actionDefinition) {
    var me = this,
        requirementsMeet = true;

    if (actionDefinition && actionDefinition.action && actionDefinition.action.requiredModules && (actionDefinition.action.requiredModules.length > 0)) {
      requirementsMeet = actionDefinition.action.requiredModules.every(function (requiredModule) {
        var modulePath, moduleObjId, moduleInstalled;
        modulePath = sol.common.RepoUtils.resolveSpecialFolder("{{bsFolderPath}}/" + requiredModule);
        moduleObjId = sol.common.RepoUtils.getObjId(modulePath);
        moduleInstalled = !!moduleObjId;
        if (!moduleInstalled) {
          me.logger.warn(["ActionDefinition of button '{0}' is missing requirements. Required module '{1}' is not installed.", actionDefinition.ribbon && actionDefinition.ribbon.button && actionDefinition.ribbon.button.name, requiredModule]);
        }
        return moduleInstalled;
      });
    }

    return requirementsMeet;
  },

  legacyMergeFct: function (actionDefinition) {
    return actionDefinition;
  },

  /**
   * @private
   * The first additional button position is used instead of the original, in case there is any.
   * The additional 'ribbonTab' and 'buttongroup' properties will replace the original, while the 'button' property will be merged into the original.
   * @param {Object} actionDefinition
   * @return {Object}
   */
  replaceMergeFct: function (actionDefinition) {
    var ribbonDef, additionalRibbonDef, mergeObjFct, i;

    mergeObjFct = function (source, target) {
      var key;
      for (key in source) {
        if (source.hasOwnProperty(key)) {
          target[key] = source[key];
        }
      }
    };
    if (actionDefinition) {
      if (actionDefinition.ribbon) {
        actionDefinition.ribbons = [actionDefinition.ribbon];
      }

      for (i = 0; i < actionDefinition.ribbons.length; i++) {
        ribbonDef = actionDefinition.ribbons[i];

        if (ribbonDef.additionalButtonPositions && ribbonDef.additionalButtonPositions[0]) {
          additionalRibbonDef = ribbonDef.additionalButtonPositions[0];
          if (additionalRibbonDef.ribbonTab && ribbonDef.ribbonTab) {
            ribbonDef.ribbonTab = additionalRibbonDef.ribbonTab;
          }
          if (additionalRibbonDef.buttongroup && ribbonDef.buttongroup) {
            ribbonDef.buttongroup = additionalRibbonDef.buttongroup;
          }
          if (additionalRibbonDef.button && ribbonDef.button) {
            mergeObjFct(additionalRibbonDef.button, ribbonDef.button);
          }
        }

      }
    }
    return actionDefinition;
  },

  multiMergeFct: function (actionDefinition) {
    throw "NotImplementedException: multiple button positions have to be implemented for this ELO version";
  }

});

/**
 * @member sol.common.ix.services.ActionDefinitionCollector
 * @method RF_sol_common_services_ActionDefinitionCollector
 * @static
 * @inheritdoc sol.common.ix.ServiceBase#RF_ServiceBaseName
 */
function RF_sol_common_services_ActionDefinitionCollector(ec, configAny) {
  var config, actionDefinitionCollector, result;

  logger.enter("RF_sol_common_services_ActionDefinitionCollector");

  config = sol.common.ix.RfUtils.parseAndCheckParams(ec, arguments.callee.name, configAny);
  config.ec = ec;

  logger.debug(["ec.ci.language={0}", String(ec.ci.language)]);
  logger.debug(["ixConnect.loginResult.clientInfo.language={0}", String(ixConnect.loginResult.clientInfo.language)]);

  actionDefinitionCollector = sol.create("sol.common.ix.services.ActionDefinitionCollector", config);
  result = actionDefinitionCollector.execute();

  logger.exit("RF_sol_common_services_ActionDefinitionCollector");

  return result;
}