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

//@include lib_Class.js
//@include lib_sol.common.JsonUtils.js
//@include lib_sol.common.ix.RfUtils.js
//@include lib_sol.common.IxUtils.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.WfUtils.js
//@include lib_sol.common.Template.js
//@include lib_sol.common.ix.FunctionBase.js
//@include lib_sol.common.Injection.js

/**
 * To be executed on a sord[1] which contains one or more fields[2] which contain handlebars templates.
 * The handlebars template may contain fields from multiple sords[3] (sord.objKeys, CANDIDATE.objKeys, POSTING.objKeys ...).
 * For each of these sords, a search instruction [4] must be defined. The search must be enriched with data of sord[1].
 * The fields'[2] handlebars template strings will then be rendered using the data which was fetched from the sords[3].
 * Finally, the rendered strings are saved to the sord[1].
 *
 * In the learning solution, this is used to render e.g. personalized e-mail templates which are sent to the course participants.
 *
 * A search instruction [4] may consist of the parameters defined in {@link sol.common.ix.services.SordProvider SordProvider}.
 *
 * @author ESt, ELO Digital Office GmbH
 * @version 1.0
 *
 * @eloix
 *
 * @requires sol.common.JsonUtils
 * @requires sol.common.ix.RfUtils
 * @requires sol.common.IxUtils
 * @requires sol.common.ix.FunctionBase
 */
sol.define("sol.common.ix.functions.RenderTemplate", {
  extend: "sol.common.ix.FunctionBase",

  mixins: ["sol.common.mixins.Inject"],

  requiredConfig: ["templateFields"],

  inject: {
    templateFields: { prop: "templateFields", forTemplating: false },
    searches: { prop: "searches", forTemplating: false, template: true, emptyNonRendered: true },
    redactor: { prop: "redactor", forTemplating: false },
    sord: { sordIdFromProp: "objId", flowIdFromProp: "flowId", includeBlobs: true }
  },

  // checks if all mandatory properties are available in the sord.
  checkSordValues: function (sord, paths, optional) {
    var me = this, ok = true;
    paths
      .forEach(function (path) {
        if (!(String(sol.common.ObjectUtils.getProp(sord, path) || ""))) {
          if (optional) {
            me.logger.warn("Optional property " + path + " not found in sord! Skipping search ...");
            ok = false;
          } else {
            throw "A search field did not have a value! Aborting ... Field path:" + path;
          }
        }
      });
    return ok;
  },

  // checks if all mandatory search properties are not empty.
  finalizeSearchValues: function (search) {
    var me = this;
    return search
      .filter(function (criterion) {
        if (!criterion.value) {
          if (criterion.optional) {
            me.logger.warn("Optional property " + criterion.key + " not included in search because it was empty!");
            return false;
          } else {
            throw "A search field did not have a value! Aborting ... Field:" + criterion.key;
          }
        }
        return true;
      });
  },

  getObjIdOf: function (config) {
    var me = this;
    if (me.checkSordValues(me.sord, config.const.checkSordValues, !!config.const.optional)) {
      config.output = [{ source: { type: "SORD", key: "id" } }];
      config.search = me.finalizeSearchValues(config.search);
      config.options || (config.options = {});
      config.options.maxResults = 1;
      return sol.common.IxUtils.execute("RF_sol_common_service_SordProvider", config).sords[0];
    }
  },

  addSordsToTemplatingData: function (templatingData, sordNames) {
    var me = this;
    sordNames
      .forEach(function (sordName) {
        var config = me.searches[sordName], objId = me.getObjIdOf(config);
        if (!objId) {
          if (!config.const.optional) {
            throw "Could not find " + sordName;
          }
        } else {
          templatingData[sordName] = sol.common.WfUtils.getTemplateSord(
            ixConnect.ix().checkoutSord(objId, SordC.mbAllIndex, LockC.NO)
          ).sord;
        }
      });
  },

  getTplPath: function (field) {
    var t = field.type;
    return field.templatePath
      || ("sord." + (
        ((t === "GRP") && "objKeys.")
          || ((t === "MAP") && "mapKeys.")
          || ((t === "WFMAP") && "wfMapKeys.")
          || ((t === "FORMBLOB") && "formBlobs.")
          || ""
      ) + field.key
      );
  },

  renderTemplateFields: function (fields, templatingData) {
    var me = this;
    fields = fields
      .map(function (field) {
        field.value = String(sol.common.ObjectUtils.getProp(templatingData, me.getTplPath(field)) || "");
        return field;
      })
      .map(function (field) {
        field.value = me.renderValue(field, templatingData);
        return field;
      })
      .map(function (field) {
        // rerender value so that templateFields are rendered twice to support template fields which also contain handlebar expressions
        if (me.shouldRerenderValue(field)) {
          field.value = me.renderValue(field, templatingData);
        }
        return field;
      });
  },

  shouldRerenderValue: function (field) {
    var me = this;

    return (me.rerenderValues || field.rerenderValue)
    && (field.value || "").indexOf("{{");
  },

  renderValue: function (field, templatingData) {
    var me = this;

    return sol.common.TemplateUtils.render(
      field.value,
      templatingData,
      { emptyNonRendered: !me.keepUnrenderedTemplates }
    );
  },

  addRedactorTemplates: function (templateFields, templatingData, targetBlob, clips) {
    var clipStr;
    if (!targetBlob) {
      throw "A taget BLOB-fieldname must be defined in redactor.storeInBlob config property!";
    }
    clipStr = sol.common.JsonUtils.stringifyQuick((sol.common.IxUtils.execute("RF_sol_common_service_GetRedactorTemplates", {
      templates: clips,
      render: { data: templatingData }
    }) || {}).templates || []);

    templateFields.push({ type: "FORMBLOB", key: targetBlob, value: clipStr });
  },

  process: function () {
    var me = this,
        templatingData = me.$templatingData,
        templateFields = me.templateFields,
        redactor = me.redactor;

    if (!Array.isArray(templateFields) || !templateFields.length) {
      throw "No fields were defined for rendering!";
    }

    templatingData.wfOwner = me.wfOwnerName;

    me.excludeUserData || (templatingData.ELOUSER = sol.common.WfUtils.getTemplateSord(sol.common.UserUtils.getUserFolder(ixConnect.loginResult.user.name)).sord);
    me.excludeSord && (templatingData.sord = undefined);
    me.excludeStaticData || (templatingData.CUSTOM = me.staticData);
    me.dontSearchForAdditionalData || me.addSordsToTemplatingData(templatingData, Object.keys(me.searches || {}));
    me.sordIsAlso && (templatingData[String(me.sordIsAlso)] = me.sord);

    me.renderTemplateFields(templateFields, templatingData);
    redactor && me.addRedactorTemplates(templateFields, templatingData, String(redactor.storeInBlob || ""), redactor.clips);

    sol.common.IxUtils.execute("RF_sol_function_Set", {
      objId: String(me.objId),
      flowId: String(me.flowId),
      entries: templateFields
    });
  }
});

/**
 * @member sol.common.ix.functions.RenderTemplate
 * @static
 * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
 */
function onEnterNode(_clInfo, _userId, wfDiagram, nodeId) {
  var params;

  sol.common.WfUtils.checkMainAdminWf(wfDiagram);
  params = sol.common.WfUtils.parseAndCheckParams(wfDiagram, nodeId),

  params.objId = params.objId || wfDiagram.objId;
  params.flowId = params.flowId || wfDiagram.id;
  params.wfOwnerName = String(wfDiagram.ownerName);

  (sol.create("sol.common.ix.functions.RenderTemplate", params)).process();
}

/**
 * @member sol.common.ix.functions.RenderTemplate
 * @static
 * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
 */
function onExitNode(_clInfo, _userId, wfDiagram, nodeId) {
  var params;

  sol.common.WfUtils.checkMainAdminWf(wfDiagram);
  params = sol.common.WfUtils.parseAndCheckParams(wfDiagram, nodeId),

  params.objId = params.objId || wfDiagram.objId;
  params.flowId = params.flowId || wfDiagram.id;
  params.wfOwnerName = String(wfDiagram.ownerName);

  (sol.create("sol.common.ix.functions.RenderTemplate", params)).process();
}