//@include lib_Class.js

/**
 * Document generation.
 *
 * This class can be used to render different kinds of documents from templates.
 *
 * To be more flexible, it uses a data collector, to collect the data which can be included in the document, and a renderer to render the resulting document.
 *
 * #Data collection
 * Data collection can either be implemented with a class or with a registered function.
 *
 * #Rendering
 * For rendering a class has to be specified. This class needs to implement a `render` function which has a name and the data (result from data collector) as arguments.
 *
 * #Ordering
 * The ordering of the data, should be determined by the data collector. Anyway, there are possible scenarios (e.g. reusing a generic data collector)
 * where it will be necessary to apply a different ordering. To accomplish this, a {@link #compareFct compare function} can be implemented.
 * This function will be applied an the data collector result after the collection.
 * It will be called with two {@link sol.common.ObjectFormatter.TemplateSord TemplateSord} as arguments (see {@link #compareFct}).
 *
 * #Configuration
 * The configuration for the used modules has to be defined during construction of the `DocumentGenerator`.
 * The defined objects `collectorConfig` and `rendererConfig` will be handed directly to the used modules and therefor their structure is dependent on the used module.
 *
 * #Examples
 *
 * The first example will generate a word document with the data returned by the `ParentDataCollector`:
 *
 *     var generator = sol.create("sol.common.as.DocumentGenerator", {
 *       name: name,
 *       dataCollector: "RF_sol_pubsec_service_ParentDataCollector",
 *       renderer: "sol.common.as.renderer.Word",
 *       collectorConfig: {
 *         objId: me.objId,
 *         returnDataDefinition: true
 *       },
 *       rendererConfig: {
 *         objId: me.objId,
 *         templateId: me.templateId
 *       }
 *     });
 *     var result = generator.process();
 *
 * The next example will generate a PDF report with all child elements having a defined mask. The result of the data collector will be sorted using the `compareFct`:
 *
 *     var generator = sol.create("sol.common.as.DocumentGenerator", {
 *       name: name,
 *       dataCollector: "sol.common.ix.services.ChildrenDataCollector",
 *       renderer: "sol.common.as.renderer.Fop",
 *       collectorConfig: {
 *         parentId: me.parentId,
 *         endLevel: -1,
 *         objKeys: [],
 *         totalCount: 50000,
 *         sordKeys: ['ownerName', 'name', 'maskName', 'maskId', 'id', 'guid', 'parentId', 'XDateIso', 'IDateIso'],
 *         maskName: me.config.filingPlan.maskName,
 *         formatter: "sol.common.ObjectFormatter.TemplateSord"
 *       },
 *       rendererConfig: {
 *         targetId: me.targetId,
 *         templateId: me.templateId
 *       },
 *       compareFct: function (templateSord1, templateSord2) {
 *         var result;
 *         try {
 *           result = templateSord1.objKeys.FILING_PLAN_REFERENCE.localeCompare(templateSord2.objKeys.FILING_PLAN_REFERENCE);
 *         } catch (ex) {
 *           result = 0;
 *         }
 *         return result;
 *       }
 *     });
 *     var result = generator.process();
 *
 * #DocumentGenerator result
 * The result object is the object returned by the `render` function of the renderer.
 *
 * @author PZ, ELO Digital Office GmbH
 * @version 1.1
 *
 * @eloas
 * @requires sol.common.ObjectUtils
 * @requires sol.common.ObjectFormatter
 * @requires sol.common.StringUtils
 * @requires sol.common.IxUtils
 * @requires sol.common.SordUtils
 */
sol.define("sol.common.as.DocumentGenerator", {

  requiredConfig: ["dataCollector", "renderer", "name"],

  /**
   * @cfg {String} dataCollector (required)
   */

  /**
   * @cfg {Boolean} returnData
   * Return the data in `result.data`
   */

  /**
   * @cfg {String} renderer (required)
   */

  /**
   * @cfg {String} name (required)
   * The name for the document
   */

  /**
   * @cfg {Object} collectorConfig
   * The configuration handed to the renderer module/function
   */

  /**
   * @cfg {Object} rendererConfig
   * The configuration handed to the renderer module
   */

  /**
   * @cfg {Function} compareFct
   * Function to sort the `dataCollector` result.
   *
   * The function will be called with to {@link sol.common.ObjectFormatter.TemplateSord TemplateSord} objects
   * and has to return `-1` if first TemplateSord is bigger then the second one, `1` in the opposite case, or `0` if they are equal.
   *
   *      function (templateSord1, templateSord2) {
   *        return templateSord1.objKeys.FILING_PLAN_REFERENCE.localeCompare(templateSord2.objKeys.FILING_PLAN_REFERENCE);
   *      }
   */

  /**
   * @cfg {Boolean} restrictRightsToCurrentUser
   * Restricts the rights of the document to the current user
   */

  /**
   * @cfg {Object} additionalSordData
   * Definitions of additional Sord data to be provided, e.g. filter data for reports
   *
   *     additionalSordData: [{
   *       objId: me.objId,
   *       propertyName: "settings"
   *     }]
   */

  /**
   * @cfg {String} additionalSordData[].objId
   * Object ID of the Sord whose data is to be provided
   */

  /**
   * @cfg {String} additionalSordData[].propertyName
   * Name of the property in which the additional data is provided
   */

  initialize: function (config) {
    var me = this;
    me.$super("sol.Base", "initialize", [config]);

    me.initDataCollector(config);
    me.initRenderer(config);
  },

  process: function () {
    var me = this,
        data, result, additionalSord, additionalTplSord, i, additionalSordDataDef;

    data = me.dataCollector.collect();

    if (sol.common.ObjectUtils.isFunction(me.compareFct) && data && data.sords && (data.sords.length > 0)) {
      data.sords.sort(me.compareFct);
    }

    me.logger.debug(["additionalSordData: config={0}", JSON.stringify(me.additionalSordData) || ""]);

    if (me.additionalSordData) {
      for (i = 0; i < me.additionalSordData.length; i++) {
        additionalSordDataDef = me.additionalSordData[i];
        if (additionalSordDataDef && additionalSordDataDef.objId && additionalSordDataDef.propertyName) {
          additionalSord = sol.common.RepoUtils.getSord(additionalSordDataDef.objId);
          additionalTplSord = sol.common.SordUtils.getTemplateSord(additionalSord).sord;
          me.logger.debug(["additionalSordData: propertyName={0}, data={1}", additionalSordDataDef.propertyName, JSON.stringify(additionalTplSord) || ""]);
          data[additionalSordDataDef.propertyName] = additionalTplSord;
        }
      }
    }

    result = me.renderer.render(me.name, data);

    if (result.objId && me.restrictRightsToCurrentUser) {
      sol.common.AclUtils.changeRightsInBackground(result.objId, {
        users: [EM_USERID],
        rights: { r: true, w: true, d: true, e: true, l: true, p: true },
        mode: "SET"
      });
    }

    if (me.returnData) {
      result.data = data;
    }

    return result;
  },

  /**
   * @private
   * @param {Object} config
   */
  initDataCollector: function (config) {
    var me = this;
    if (!sol.common.ObjectUtils.isObject(me.dataCollector)) {
      if (sol.common.StringUtils.startsWith(me.dataCollector, "RF_")) {
        me.dataCollector = {
          _conf: config.collectorConfig,
          _rf: me.dataCollector,
          collect: function () {
            var me = this;
            return sol.common.IxUtils.execute(me._rf, me._conf);
          }
        };
      } else {
        me.dataCollector = sol.create(me.dataCollector, config.collectorConfig);
      }
    }
    if (!sol.common.ObjectUtils.isFunction(me.dataCollector.collect)) {
      throw "dataCollector has to define a 'collect' function";
    }
  },

  /**
   * @private
   * @param {Object} config
   */
  initRenderer: function (config) {
    var me = this;
    if (!sol.common.ObjectUtils.isObject(me.renderer)) {
      me.renderer = sol.create(me.renderer, config.rendererConfig);
    }
    if (!sol.common.ObjectUtils.isFunction(me.renderer.render)) {
      throw "renderer has to define a 'render' function";
    }
  }
});