//@include lib_Class.js

/**
 * Utilities to interact with the ELO Automation Services
 *
 * @author MW, ELO Digital Office GmbH
 * @version 1.03.002
 *
 * @elojc
 * @eloas
 * @eloix
 *
 * @requires sol.common.Config
 * @requires sol.common.Template
 * @requires sol.common.WfUtils
 * @requires sol.common.HttpUtils
 * @requires sol.common.JsonUtils
 * @requires sol.common.ExceptionUtils
 */
sol.define("sol.common.AsUtils", {
  singleton: true,

  /**
   * @private
   */
  logger: sol.create("sol.Logger", { scope: "sol.common.AsUtils" }),

  /**
   * Returns the guessed ELOas base URL
   * @return {String} url. ELOas base URL.
   */
  guessAsBaseUrl: function () {
    var ixBaseUrl, asBaseUrl;

    ixBaseUrl = ixConnect.endpointUrl + "";
    ixBaseUrl = ixBaseUrl.replace(/\/ix$/, "");
    asBaseUrl = ixBaseUrl.replace(/\/ix/, "/as");

    return asBaseUrl;
  },

  /**
   * Tests a given ELOas URL
   * @param {String} url
   *
   *     var testResult = sol.common.AsUtils.testAsBaseUrl('http://elosrv01:8080/as-archive/');
   *     if (testResult.asUrlTestOk) {
   *       // valid url
   *     }
   *
   * @return {Object} response
   */
  testAsBaseUrl: function (url) {
    var httpResponse;

    httpResponse = sol.common.HttpUtils.sendRequest({ url: url, resolve: false, connectTimeout: 3000, readTimeout: 10000 });
    httpResponse.asUrlTestOk = (httpResponse.responseOk && (httpResponse.content.indexOf("ELO Automation Services") > -1));

    return httpResponse;
  },

  /**
   * Converts an ELOas URL to a configuration object.
   *
   *     sol.common.AsUtils.convertAsUrlToConfigObject('http://elosrv01:8080/as-archive');
   *     {
   *       protocol: 'http',
   *       server: 'elosrv01',
   *       port: '8080',
   *       name: 'as-archive'
   *     }
   *
   * @param {String} url ELOas URL
   * @return {Object} ELOas configuration object}
   */
  convertAsUrlToConfigObject: function (url) {
    var urlParts, asConfig;

    if (url.indexOf("de.elo.ix.plugin.proxy") > -1) {
      urlParts = url.match(/^(http|https):\/\/([a-z0-9.\-_]+):?([0-9]+)?\/([a-z0-9.\-_/]+de.elo.ix.plugin.proxy\/[a-z0-9.\-_]+)/i);
    } else {
      urlParts = url.match(/^(http|https):\/\/([a-z0-9.\-_]+):?([0-9]+)?\/([a-z0-9.\-_]+)/i);
    }

    if (!urlParts) {
      throw "Can't parse ELOas URL.";
    }
    asConfig = { protocol: urlParts[1], serverName: urlParts[2], port: urlParts[3], serviceName: urlParts[4] };

    return asConfig;
  },

  /**
   * Calls an ELOas rule
   *
   * @param {Object} config Configuration
   * @param {String} params.protocol Protocol
   * @param {String} params.serverName Server name
   * @param {String} params.port Port
   * @param {String} params.serviceName Service name
   * @param {String} config.ruleName Rule name
   * @param {String} config.ticket (optional) Adds the specified ticket
   * @param {String} [config.cmd=get] `run` for an asynchronous call, `get` for a synchronous call
   * @param {String} [config.solutionNameForAsConfig=common] This will be used to load the ELOas server configuration
   * @param {Boolean} [config.expectJsonResponse=true] Expect JSON response
   * @param {Boolean} [config.addTicket=true] Adds the user ticket
   * @param {Boolean} [config.throwException=true] If `true` then a exception will be thrown if an error occurs.
   * @param {String} params.param1 Parameter 1
   * @param {String} params.param2 Parameter 2
   * @param {Object} params.param2Obj Parameter object 2
   * @param {String} params.param3 Parameter 3
   * @param {Object} params.param3Obj Parameter object 3
   * @return {Object} result
   *
   */
  callAs: function (config) {
    var me = this,
        asUrl, logUrl, result, errorMessage, content;

    if (!config) {
      throw "Configuration is empty";
    }

    config.throwException = (config.throwException == false) ? false : true;
    config.addTicket = (config.addTicket == false) ? false : true;
    config.expectJsonResponse = (config.expectJsonResponse == false) ? false : true;

    if (!config.ruleName) {
      throw "Rule name is empty";
    }

    asUrl = me.buildAsUrl(config);
    me.obfuscateTicket(config);
    logUrl = me.buildAsUrl(config);

    me.logger.debug("Executing ELOas call: ", asUrl);

    result = sol.common.HttpUtils.sendGet(asUrl, {
      connectTimeout: 30000,
      readTimeout: 30000,
      contentType: "application/json;charset=UTF-8"
    });

    if (result.errorMessage) {
      if (result.errorMessage.message) {
        errorMessage = me.formatErrorMessage("HTTP error", [result.errorMessage.message + ": " + result.errorMessage.fileName + "#" + result.errorMessage.lineNumber]);
      } else {
        me.logger.error(["HTTP error: {0}, url={1}", result.errorMessage, logUrl]);
        errorMessage = me.formatErrorMessage("HTTP error", [result.errorMessage]);
      }
    } else if (!result.content) {
      errorMessage = me.formatErrorMessage("ELOas result is empty");
    } else if (result.content.indexOf("<h1>HTTP Status 500") > -1) {
      errorMessage = me.formatErrorMessage("ELOas exception", [result.content]);
    } else if (result.content.indexOf("Error") == 0) {
      errorMessage = me.formatErrorMessage("ELOas error", [result.content]);
    } else if (result.content.indexOf("undefined") == 0) {
      errorMessage = me.formatErrorMessage("ELOas result is 'undefined'");
    } else if (result.content.indexOf("Wait...") == 0) {
      errorMessage = me.formatErrorMessage("ELOas doesn't respond.");
    }

    if (!errorMessage && (config.cmd == "get") && config.expectJsonResponse) {
      try {
        content = JSON.parse(result.content);
        if (content.exception) {
          errorMessage = content.exception;
        }
      } catch (ex) {
        errorMessage = me.formatErrorMessage("Unexcepted response", [result.content]);
      }
    }

    if (errorMessage) {
      result.errorMessage = errorMessage;
      if (config.throwException) {
        throw errorMessage;
      }
    }

    return result || {};
  },

  /**
   * @private
   * Formats the error message
   * @param {String} message Message
   * @param {Array} lines Lines
   * @param {Object} params Parameters
   * @param {String} [params.format=JC] Format
   * @return {String} Error message
   */
  formatErrorMessage: function (message, lines, params) {
    var errorMessage;

    lines = lines || [];

    params = params || {};
    params.format = params.format || "JC";

    if (params.format == "JC") {
      errorMessage = "<h3>" + message + "</h3>";
      if (lines.length > 0) {
        errorMessage += lines.join("<br>") + "<br>";
      }
    } else {
      errorMessage = message;
      if (lines.length > 0) {
        errorMessage += ": " + lines.join("\r\n") + "\r\n";
      }
    }

    return errorMessage;
  },

  /**
   * @deprecated
   * Returns the ELOas URL
   *
   * @param {Object} config Configuration. The configuration will be send to ELOas via HTTP parameter 'param2'
   * @param {String} config.ruleName
   * @param {String} [config.mode="get"] (optional) "run" for an asynchronous call, "get" for a synchronous call
   * @param {String} [config.solutionNameForAsConfig="common"] (optional) This will be used to load the ELOas server configuration
   * @return {String}
   *
   * Example:
   *
   */
  getAsUrl: function (config) {
    var solutionName, mode, ruleName, asCfg, asUrlTpl, asUrl, objId,
        asUrlTplStringHead = "{{asCfg.protocol}}://{{asCfg.serverName}}:{{asCfg.port}}/{{asCfg.serviceName}}/as?cmd={{asCall.mode}}&name={{asCall.ruleName}}",
        asUrlTplStringTail = "&param2={{asCall.config}}&ticket={{ticket}}",
        asUrlTplString = asUrlTplStringHead + asUrlTplStringTail;

    if (!config) {
      throw "Configuration is empty";
    }

    if (!config.ruleName) {
      throw "Rule name is empty";
    }

    if (config.objId) {
      objId = config.objId;
      delete config.objId;
      asUrlTplString = asUrlTplStringHead + "&param1={{asCall.objId}}" + asUrlTplStringTail;
    }

    solutionName = config.solutionNameForAsConfig || "common";
    delete config.solutionNameForAsConfig;

    mode = config.mode || "get";
    delete config.mode;

    ruleName = config.ruleName;
    delete config.ruleName;

    asCfg = sol.create("sol.common.Config").loadEloAsConfig(solutionName);

    asUrlTpl = sol.create("sol.common.Template", { source: asUrlTplString });
    asUrl = asUrlTpl.apply({
      ticket: ixConnect.loginResult.clientInfo.ticket,
      asCfg: asCfg,
      asCall: {
        mode: mode,
        ruleName: encodeURIComponent(ruleName),
        objId: objId,
        config: encodeURIComponent(sol.common.JsonUtils.stringifyAll(config))
      }
    });
    return asUrl;
  },

  /**
   * Builds an ELOas URL
   * @param {Object} params Parameters
   * @param {String} params.solutionNameForAsConfig Solution name for AS config
   * @param {String} params.protocol Protocol
   * @param {String} params.serverName Server name
   * @param {String} params.port Port
   * @param {String} params.serviceName Service name
   * @param {String} [params.cmd=get] Command, e.g. ´run´ or ´get´
   * @param {String} params.ruleName Rule name
   * @param {String} params.param1 Parameter 1
   * @param {String} params.param2 Parameter 2
   * @param {Object} params.param2Obj Parameter object 2
   * @param {String} params.param3 Parameter 3
   * @param {Object} params.param3Obj Parameter object 3
   * @param {String} params.ticket (optional) Specific ticket
   * @param {Boolean} addTicket If true the session ticket will be added
   * @return {String} URL
   */
  buildAsUrl: function (params) {
    var me = this,
        urlParams = [],
        url, additionalParams;

    me.prepareParameter(params);
    me.checkParams(params);

    urlParams.push("cmd=" + params.cmd);
    urlParams.push("name=" + encodeURIComponent(params.ruleName));

    if (params.param1) {
      urlParams.push("param1=" + params.param1);
    } else if (params.objId) {
      urlParams.push("param1=" + params.objId);
    }

    additionalParams = me.prepareUrlParams(params);

    if (additionalParams.length > 0) {
      urlParams = urlParams.concat(additionalParams);
    } else {
      urlParams.push("param2=" + encodeURIComponent(me.prepareParam2(params)));
    }

    if (!params.ticket && params.addTicket) {
      params.params3 = params.params3 || {};
      params.params3.language = (params.params3.language || ixConnect.loginResult.clientInfo.language) + "";
      params.params3.timeZone = (params.params3.timeZone || ixConnect.loginResult.clientInfo.timeZone) + "";

      params.ticket = ixConnect.loginResult.clientInfo.ticket + "";
    }

    if (params.params3) {
      urlParams.push("param3=" + encodeURIComponent(JSON.stringify(params.params3)));
    }

    if (params.ticket) {
      urlParams.push("ticket=" + params.ticket);
    }

    url = params.protocol + "://" + params.serverName + ":" + params.port + "/" + params.serviceName + "/as?" + urlParams.join("&");

    return url;
  },

  /**
   * Obfuscate ticket
   * @param {Object} config Configuration
   */
  obfuscateTicket: function (config) {
    config = config || {};
    config.ticket += "";
    if (config.ticket && config.ticket.length > 11) {
      config.ticket = config.ticket.substr(0, 10) + "...";
    }
  },

  /**
   * @private
   * Applies the default values and the ELOas configuration. Used by {@link #buildAsUrl}.
   * @param {Object} params
   */
  prepareParameter: function (params) {
    var solutionNameForAsConfig, asCfg;

    if (!params.serverName) {
      if (params.param2Obj && params.param2Obj.solutionNameForAsConfig) {
        solutionNameForAsConfig = params.param2Obj.solutionNameForAsConfig;
        delete params.param2Obj.solutionNameForAsConfig;
      } else {
        solutionNameForAsConfig = params.solutionNameForAsConfig;
      }
      asCfg = sol.create("sol.common.Config").loadEloAsConfig(solutionNameForAsConfig);
      params.protocol = asCfg.protocol;
      params.serverName = asCfg.serverName;
      params.port = asCfg.port;
      params.serviceName = asCfg.serviceName;
    }

    params.protocol = params.protocol || "http";

    if (!params.port && params.protocol) {
      if (params.protocol == "http") {
        params.port = 80;
      }
      if (params.protocol == "https") {
        params.port = 443;
      }
    }

    params.cmd = params.cmd || params.mode || "get";
  },

  /**
   * @private
   * Checks, if all mandatory parameters are set. Used by {@link #buildAsUrl}.
   * @param {Object} params
   */
  checkParams: function (params) {
    if (!params.serverName) {
      throw "Property 'params.serverName' is empty";
    }

    if (!params.port) {
      throw "Property 'params.port' is empty";
    }

    if (!params.serviceName) {
      throw "Property 'params.serverName' is empty";
    }

    if (!params.ruleName) {
      throw "Property 'params.port' is empty";
    }
  },

  /**
   * @private
   * Prepares the additional parameters (param2 - param10). Used by {@link #buildAsUrl}.
   * @param {Object} params
   * @return {String[]}
   */
  prepareUrlParams: function (params) {
    var additionalParams = [],
        paramName, paramValue, i;

    for (i = 2; i <= 10; i++) {
      paramName = "param" + i;
      paramValue = params[paramName];
      if (paramValue) {
        additionalParams.push(paramName + "=" + encodeURIComponent(paramValue));
        continue;
      }

      paramValue = params["param" + i + "Obj"];
      if (paramValue) {
        paramValue = sol.common.JsonUtils.stringifyAll(paramValue);
        additionalParams.push(paramName + "=" + encodeURIComponent(paramValue));
      }
    }

    return additionalParams;
  },

  /**
   * @private
   * Prepares param2 with all properties from params (cleaned). Used by {@link #buildAsUrl}.
   * @param {Object} params
   * @return {String}
   */
  prepareParam2: function (params) {
    var param2 = {},
        ignoreProps, propertyName;

    ignoreProps = [
      "param1",
      "solutionNameForAsConfig",
      "protocol",
      "serverName",
      "port",
      "serviceName",
      "cmd",
      "ruleName"
    ];

    for (propertyName in params) {
      if (params.hasOwnProperty(propertyName) && (ignoreProps.indexOf(propertyName) < 0)) {
        param2[propertyName] = params[propertyName];
      }
    }

    return JSON.stringify(param2);
  }

});