importPackage(Packages.de.elo.ix.jscript);
importPackage(Packages.de.elo.ix.scripting);

//@include lib_Class.js
//@include lib_sol.common.Config.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.TranslateTerms.js
//@include lib_sol.common.Template.js
//@include lib_sol.common.ix.DynKwlDatabaseIterator.js
//@include lib_sol.common.ix.DynKwlSearchIterator.js
//@include lib_sol.common.ix.DynKwlFindChildrenIterator.js
//@include lib_sol.common.ix.DynKwlBLPIterator.js

var kwlConfigString,  // has priority over config file, has to be valid JSON
    configPath,       // used together with 'kwlName' to determine the kwl config from a file
    kwlName,          // used together with 'configPath' to determine the kwl config from a file
    __ctx = (function () {
      return this;
    }());

/**
 * This implements generic keywordlist functionality.
 *
 * # Supported keywordlist types
 *
 * |Type|Used Iterator|
 * |:-----|:------|
 * |DB|Database iterator|
 * |SEARCH|Search iterator|
 * |CHILDREN|Children iterator|
 *
 * # Configuration
 * It is highly recommended to use the provided app to create/edit the configurations.
 *
 * Each configuration (regardless of the used iterator) has to contain the following properties:
 *
 * |Property|Property type|Description|Note|
 * |:-----|:-----|:------|:------|
 * |type|String|The type used. See `Supported keywordlist types`.||
 * |translate|Boolean|Determines, if translation should be applied on the `header`, the `output` and `searchParams.message`|Has to be `true`, if any of those contain translation keys.|
 * |title|String|The title of the returned table. Could be a fixed string or a translation key.||
 * |header|String[]|An array with the table headers. Could be a fixed strings or a translation keys.|Currently not supported in children iterator.|
 * |output|String[]|An array containing the output fields.|No prefix for index fields. `IX_MAP_` prefix for map fields. `IX_MAP_` prefix and `{i}` suffix for map fields inside a form table.|
 * |searchParams|Object[]|An array containing configuration for the search.|This uses the old syntax of the iterators. For better understanding the app should be used to create the configuration. Currently not supported in children iterator.|
 *
 * There are additional configuration properties for each iterator type.
 *
 * ## DB
 * |Property|Property type|Description|Note|
 * |:-----|:-----|:------|:------|
 * |sqlQuery|String|The SQL query for the database.|Number of selected columns has to be the same as the number of `headers` and `output`. If the query contains `?` they will be replaced by evaluating the `searchParams`.|
 * |jdbc|String|The name of a ressource defined in the Tomcat `META-INF/context.xml`.|Optional. If not set, the query will be executed against the ELO database.|
 * |dbName|String|To use a devian schema.|Optional|
 *
 * ## SEARCH
 * |Property|Property type|Description|Note|
 * |:-----|:-----|:------|:------|
 * |dataFields|String[]|The fields from the found Sord objects which will be mapped to the output fields.|Has to be of the same size as the `output` array. Only index fields are supported.|
 *
 * ## CHILDREN
 * |Property|Property type|Description|Note|
 * |:-----|:-----|:------|:------|
 * |parantId|String|The start point for the children search.|Could be an arcpath, an objId or a guid.|
 *
 * # How to
 * To implement a keywordlist using this generic class two things are necessary: a script and a configuration.
 * The script needs to be implemented, because the only way to configure a field with a dynamic keyword list, is the name of a script file.
 * The configuration will tell the generic class how to performe the queries and how the data should be returned.
 *
 * The script file has to contain the include for the `lib_Class` as well as the include for the `lib_sol.common.ix.GenericDynKwl`.
 *
 * The easiest way to include the configuration is directly as a JSON string in the script as a global variable `kwlConfigString`:
 *
 *     var kwlConfigString = '{ ... }'
 *
 * A more convinient way to include the configuration is in a separate configuration file:
 *
 *     var configPath = "/contract/Configuration/kwl.config",
 *         kwlName = "Companies";
 *
 * The `kwl.config` has to be JSON containing the property `Companies` which holds the configuration for this keyword list.
 * For even more convinience there is an app to edit those configuration file.
 *
 * @author PZ, ELO Digital Office GmbH
 * @version 1.04.000
 *
 * @eloix
 * @requires sol.common.ix.DynKwlDatabaseIterator
 * @requires sol.common.ix.DynKwlSearchIterator
 * @requires sol.common.ix.DynKwlFindChildrenIterator
 */
sol.define("sol.common.ix.GenericDynKwl", {

  initialize: function (config) {
    var me = this,
        cfg, providerCfg, prepare;

    cfg = me.loadConfig(config);
    if (!cfg) {
      throw "No configuration found. Configuration has to be provided by kwlConfigString, configPath/kwlName or contructor parameter.";
    }
    providerCfg = me.prepareConfig(cfg);

    prepare = me.prepareFunctions[cfg.type];
    if (!sol.common.ObjectUtils.isFunction(prepare)) {
      throw "IllegalConfigurationException: type '" + cfg.type + "' is not supported";
    }

    me._provider = prepare.call(me, providerCfg, cfg);

    if (cfg.columnProperties) {
      if (cfg.columnProperties.length !== cfg.output.length) {
        throw "IllegalConfigurationException: number of columnProperties has to match the number of columns.";
      }

      me._provider.getColumnProperties = function () {
        return cfg.columnProperties;
      };
    }

  },

  /**
   * Retrieves the kwl provider which was created during initialization.
   * @return {Object}
   */
  getProvider: function () {
    var me = this;
    return me._provider;
  },

  /**
   * @private
   * Initializes the configuration. Either from `kwlConfigString`, a config file or the contructor parameter.
   * @param {Object} initConfigParam
   * @return {Object}
   */
  loadConfig: function (initConfigParam) {
    var config;
    if (kwlConfigString) {
      config = JSON.parse(kwlConfigString);
    } else if (configPath && kwlName) {
      config = sol.create("sol.common.Config", { compose: configPath }).config;
      if (!config.hasOwnProperty(kwlName)) {
        throw "No configuration found for '" + kwlName + "' (path='" + configPath + "')";
      }
      config = config[kwlName];
    } else {
      config = initConfigParam;
    }

    return config;
  },

  /**
   * @private
   * Initializes the provider config for all provider types.
   * @param {Object} config
   * @return {Object}
   */
  prepareConfig: function (config) {
    var me = this,
        providerConfig;

    me.translate(config);

    providerConfig = {
      tableTitle: config.title,
      tableHeaders: config.header,
      tableKeyNames: __ctx.output || config.output,  // override default from configuration
      formatting: config.formatting
    };

    if (typeof __ctx.output === "function") {  // if output is overriden by a function instead of a property, this to properties are used by the mixin to determine the correct output fields
      providerConfig.tableKeyNames_default = config.output;
      providerConfig.tableKeyNames_configOverride = config.output_;
    }

    return providerConfig;
  },

  /**
   * @private
   * Performs the translation if `translate` is set to `true`.
   * @param {Object} config
   */
  translate: function (config) {
    var required;

    if (config.translate === true) {
      required = [config.title];

      if (config.header) { // filter null/undefined values
        config.header.forEach(function (headerEntry) {
          headerEntry && required.push(headerEntry);
        });
      }

      if (config.searchParams) { // only add key, if there is a search param with a 'message' property
        config.searchParams.forEach(function (param) {
          param && param.message && required.push(param.message);
        });
      }

      sol.common.TranslateTerms.require(required);

      config.title = sol.common.TranslateTerms.translate(config.title);
      if (config.header) {
        config.header.forEach(function (column, idx) {
          if (column) {
            config.header[idx] = sol.common.TranslateTerms.translate(column);
          }
        });
      }
      if (config.searchParams) {
        config.searchParams.forEach(function (param) {
          if (param && param.message) {
            param.message = sol.common.TranslateTerms.translate(param.message);
          }
        });
      }
    }
  },

  /**
   * @private
   * @property {Object}
   * Lookup object for the prepare functions for all supported iterators.
   * These function prepare the iterator specific configuration and create the provider.
   */
  prepareFunctions: {

    DB: function (providerConfig, config) {

      providerConfig.sqlQuery = config.sqlQuery;
      providerConfig.sqlParams = config.searchParams;
      providerConfig.dbName = config.dbName;
      providerConfig.jdbc = config.jdbc;

      return sol.create("sol.common.ix.DynKwlDatabaseIterator", providerConfig);
    },

    CHILDREN: function (providerConfig, config) {

      providerConfig.searchParams = config.searchParams;
      providerConfig.parentId = config.parentId;

      return sol.create("sol.common.ix.DynKwlFindChildrenIterator", providerConfig);
    },

    SEARCH: function (providerConfig, config) {
      var me = this,
          provider;

      providerConfig.searchParams = config.searchParams;

      provider = sol.create("sol.common.ix.DynKwlSearchIterator", providerConfig);
      provider.dataFields = config.dataFields;
      provider.getFindInfo = me.getFindInfo;
      provider.getRowData = me.getRowData;

      return provider;
    },

    BLP: function (providerConfig, config) {
      var me = this,
          provider;

      providerConfig.queryConditions = config.searchParams;
      providerConfig.queryName = config.blpQueryName;
      providerConfig.queryModule = config.blpQueryModule;
      providerConfig.addInId = config.blpAddInId;
      providerConfig.projectId = config.blpProjectId;
      providerConfig.moduleId = config.blpModuleId;
      providerConfig.appToken = config.blpAppToken;
      providerConfig.serverUrl = config.blpServerUrl;

      provider = sol.create("sol.common.ix.DynKwlBLPIterator", providerConfig);

      return provider;
    }

  },

  /**
   * @private
   * Implements a find by type search that is filtered by ObjKeys.
   * Used for the 'DynKwlSearchIterator'.
   * @param {String[]} filterList
   * @return {de.elo.ix.client.FindInfo}
   */
  getFindInfo: function (filterList) {
    var me = this,
        findInfo, findByIndex, okeys, okey, i, param, filter;

    this.log.enter("getFindInfo");

    findInfo = new FindInfo();
    findByIndex = new FindByIndex();
    okeys = [];

    if (filterList && filterList.length > 0) {
      for (i = 0; i < filterList.length; i++) {
        param = me.searchParams[i];
        filter = filterList[i];
        if (param.name && filter && (filter != "")) {
          okey = new ObjKey();
          okey.name = param.searchName || param.name;
          okey.data = [filter];
          okeys.push(okey);
        }
      }
    }

    findByIndex.objKeys = okeys;
    findInfo.findByIndex = findByIndex;

    findInfo.findOptions = new FindOptions();
    findInfo.findOptions.searchMode = SearchModeC.ONE_TERM;

    this.log.exit("getFindInfo");
    return findInfo;
  },

  /**
   * @private
   * Basic implementation for search results.
   * This returns the content of the sord index fields.
   * Used for the 'DynKwlSearchIterator'.
   * @param {de.elo.ix.client.Sord} sord
   * @return {String[]}
   */
   getRowData: function (sord) {
    var me = this,
        data = [], dataField, value,
        i;

    for (i = 0; i < me.dataFields.length; i++) {
      dataField = me.dataFields[i];

      if (sol.common.ObjectUtils.type(dataField, "string")) {
        // backwards compatibility (common <= 1.13.002)
        value = sol.common.SordUtils.getObjKeyValue(sord, me.dataFields[i]);
      } else if (dataField.type === "SORD") {
        value = String(sord[dataField.key]);
      } else {
        value = sol.common.SordUtils.getObjKeyValue(sord, dataField.key);
      }

      data.push(value);
    }

    return data;
  }

});

function getDataIterator() {
  var log = sol.create("sol.Logger", { scope: "sol.common.ix.GenericDynKwl#" + kwlName }),
      provider;

  try {
    log.info("DynamicKeywordList (");
    provider = sol.create("sol.common.ix.GenericDynKwl").getProvider();
    return new DynamicKeywordDataProvider(provider);
  } finally {
    log.info(")getDataIterator");
  }
}