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

//@include lib_Class.js
//@include lib_sol.common.Injection.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.ix.RfUtils.js
//@include lib_sol.common.ix.ServiceBase.js
//@include lib_sol.common.ElementService.js
//@include lib_sol.common.Template.js

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



/**
 * Checks whether certain conditions match at the selected Sord.
 * Both objKeys and mapKeys are supported.
 *
 * The Sord object is available as a TemplateSord object.
 * {
 *   "objKeys": {
 *       "MEETING_STATUS" : "D - Draft"
 *   },
 *   "mapKeys": {
 *       "MEETING_TYPE": "Test"
 *   }
 * }
 *
 * Within the conditions, the individual fields can be accessed via a JSON object path.
 * For example: objKeys.MEETING_STATUS or mapKeys.MEETING_TYPE
 *
 * #### Example
 *
 *     {
 *       "targetId": "4711"
 *       "conditions": [
 *         { "prop" : "objKeys.MEETING_STATUS", value: "D -*"}
 *       ],
 *       "notAllowedMessage": "sol.meeting.actions.createItem.notAllowed"
 *     }
 *
 * #### Applying a condition with multiple condition values
 *
 *     {
 *       "conditions": [
 *          { "prop" : "objKeys.MEETING_STATUS", value: ["D -*", "S -*"]}
 *       ]
 *     }
 *
 * #### Result: Valid precondition check
 *
 *     {
 *       valid: true
 *     }
 *
 * #### Result: Invalid precondition check
 *
 *    {
 *       valid: false
 *       msg: "errorMessage"
 *    }
 *
 * #### Using an elementService as data provider instead of the targetSord Object
 *
 *
 *
 *    {
 *       "conditions": [ { "prop": "sords.$length" , value: "[^0]"}],
 *       "elementService": {
 *            "name": "RF_sol_common_service_SordProvider",
 *            "args": {
 *                 ...
 *                 search: [
 *                    {  {"key": "MEETINGTYPE_CODE", "value": "{{sord.objKeys.MEETINGTYPE_CODE}}"} }
 *                 ]
 *            }
 *        },
 *        "options": {
 *            "renderArgsWithElement": true
 *        }
 *    }
 *
 *
 * @author MH, ELO Digital Office GmbH
 * @author PB, ELO Digital Office GmbH
 * @version 1.00.000
 *
 * @since 1.12.000
 *
 * @eloix
 * @requires sol.common.ObjectUtils
 * @requires sol.common.Injection
 * @requires sol.common.ix.RfUtils
 * @requires sol.common.ix.ServiceBase
 * @requires sol.common.Template
 */
sol.define("sol.common.ix.services.StandardPreconditions", {
  extend: "sol.common.ix.ServiceBase",
  mixins: [
    "sol.common.mixins.Inject",
    "sol.common.mixins.ElementService", //  search sords provider
    "sol.common.mixins.ObjectFilter" // condition generator
  ],

  requiredConfig: ["targetId", "ci"],

  inject: {
    targetSord: { sordIdFromProp: "targetId" }
  },

  /**
   * @cfg {String} targetId is the targetSord to check
   */

  /**
   * @cfg {String} notAllowedMessage (optional) localization key to use if the check is invalid.
   */

  /**
    * @cfg {Array<Object>} conditions
    */


  /**
    * Performes the checks and retrieves the data as spezified in the configuration.
    *
    * Returns the value in the format:
    *   {
    *     valid: Boolean,
    *     message: String
    *   }
    * @returns {Object}
  */
  process: function () {
    var me = this,
        context = me.getContext(),
        result = { valid: false, targetId: me.targetId };

    // user has the ability to do a precondition about a set of sords
    // instead of the current sord object

    me.logger.debug(["context {0}", JSON.stringify(context)]);
    if (me.isValid(context, me.conditions || [])) {
      result.valid = true;
    } else {
      result.msg = me.translateNotAllowedMessage(me.notAllowedMessage);
    }

    if (me.$debug) {
      result.context = context;
      result.search = sol.common.TemplateUtils.render(me.elementService.args, { sord: me.targetSord });
    }

    logger.debug(["precondition check result: {0}", sol.common.ix.RfUtils.stringify(result)]);
    return result;
  },

  getContext: function () {
    var me = this,
        sords;

    if (me.elementService) {
    // use elementservice result as context
    // condition could check array size as well (props: sords.$length, value: "[^0]")
    // otherwise the sord object itself is the context
      sords = me.performElementService(me.elementService, { sord: me.targetSord }, me.options);
    }

    return sords
      ? { sords: sords }
      : me.targetSord;
  },

  isValid: function (sord, conditions) {
    var me = this;

    if (!me.hasConditions(conditions)) {
      // no conditions - precondition should always be valid.
      return true;
    }

    // from mixin sol.common.mixins.ObjectFilter
    return me.matchObject(me.generateConditions(conditions), sord);
  },

  /**
   * @private
   * @param {*} conditions
   * @returns {Boolean}
   */
  hasConditions: function (conditions) {
    return conditions
      && sol.common.ObjectUtils.isArray(conditions)
      && conditions.length > 0;
  },

  /**
   * generatedConditions has same structure like foreach or sordprovider filter
  *  but in this context condition is better naming for the purposes.
   * @param {*} conditions
   * @returns {Object} the generated filters
   */
  generateConditions: function (conditions) {
    var me = this;
    // from mixin sol.common.mixins.ObjectFilter
    return me.generateFilter(conditions);
  },

  translateNotAllowedMessage: function (message) {
    return sol.common.TranslateTerms.translate(
      message || "sol.common.ix.actions.notAllowedMessage"
    );
  },

  /**
   * @private
   * @param {Object} elementServiceCfg
   * @param {Object} templateData
   * @param {Object} options
   * @returns {{sords:Array<Object>}} elements
   */
  performElementService: function (elementServiceCfg, templateData, options) {
    var me = this, config, freshArgs;


    if (options.renderArgsWithElement) {
      freshArgs = sol.common.TemplateUtils.render(elementServiceCfg.args, templateData);
      elementServiceCfg.args = freshArgs;
    }
    me.logger.debug(["performElementService"]);
    config = me.sanitizeElementServiceConfig(elementServiceCfg);
    return me.executeElementService(config);
  }
});

/**
 * @member sol.common.ix.services.StandardPreconditions
 * @method RF_sol_common_service_StandardPreconditions
 * @static
 * @inheritdoc sol.common.ix.ServiceBase#RF_ServiceBaseName
 */
function RF_sol_common_service_StandardPreconditions(ec, args) {
  var config, service, result;

  logger.enter("RF_sol_common_service_StandardPreconditions", args);

  config = sol.common.ix.RfUtils.parseAndCheckParams(
    ec,
    arguments.callee.name,
    args
  );
  config.ci = ec.ci;
  config.user = ec.user;
  service = sol.create("sol.common.ix.services.StandardPreconditions", config);
  result = sol.common.ix.RfUtils.stringify(service.process());

  logger.exit("RF_sol_common_service_StandardPreconditions", result);

  return result;
}