//@include lib_Class.js

/**
 * @private
 * Global IX service registry
 *
 * @author PZ, ELO Digital Office GmbH
 * @version 1.05.000
 *
 * @eloix
 *
 * @requires sol.common.SordUtils
 * @requires sol.common.Config
 * @requires sol.common.Template
 */
sol.define("sol.common.ix.ServiceRegistry", {
  singleton: true,

  TYPES: {
    SERVICE: "SERVICE",
    CONFIG: "CONFIG"
  },

  FUNCTIONS: {
    CREATE: "CREATE",
    IMPORT: "IMPORT",
    REGISTER_UPDATE: "REGISTER_UPDATE"
  },

  /**
   * @cfg {Boolean} includeMaskFields
   * If `true` the mask line information (if the service defines any) will be included.
   * Only used if a specific service is queried (`serviveId` instead of `query`).
   */

  /**
   * @cfg {Boolean} summarize
   * If `true` the queries will return a summary of the found services.
   */

  /**
   * @cfg {Boolean} evaluateConfig
   * If `true`, and the results are from type `config`, the config will be evaluated using a configured file.
   */

  /**
   * @private
   * @property {Object} docMaskCache
   * Caches the doc mask info.
   */
  docMaskCache: {},

  getServiceRegistry: function () {
    globalScope.$registries = globalScope.$registries || {};
    globalScope.$registries.services = globalScope.$registries.services || {};

    return globalScope.$registries.services;
  },

  register: function (serviceId, serviceDescription) {
    var me = this,
        serviceRegistry;

    serviceRegistry = me.getServiceRegistry();

    if (serviceRegistry[serviceId]) {
      throw "Service with id '" + serviceId + "' is already defined";
    }

    me.checkServiceDescription(serviceDescription);

    serviceRegistry[serviceId] = {
      serviceDescription: serviceDescription
    };

  },

  get: function (serviceId, params) {
    var me = this,
        serviceRegistry, result;

    serviceRegistry = me.getServiceRegistry();
    result = me.buildResult(serviceId, serviceRegistry[serviceId].serviceDescription, {
      includeMaskFields: params.includeMaskFields,
      summarize: false,
      evaluateConfig: params.evaluateConfig
    });

    return serviceRegistry[serviceId] ? [result] : [];
  },

  query: function (query, params) {
    var me = this,
        services = [],
        serviceRegistry, serviceId;

    serviceRegistry = me.getServiceRegistry();
    params = params || {};

    for (serviceId in serviceRegistry) {
      if (serviceRegistry.hasOwnProperty(serviceId) && me.matchQueryParams(query, serviceRegistry[serviceId].serviceDescription)) {
        services.push(
          me.buildResult(serviceId, serviceRegistry[serviceId].serviceDescription,
            {
              includeMaskFields: false,
              summarize: params.summarize,
              evaluateConfig: params.evaluateConfig
            }
          )
        );
      }
    }

    return services;
  },

  /* @private */
  checkServiceDescription: function (desc) {
    if (!desc.type || !desc.name || !desc.ns) {
      throw "IllegalConfigurationException: service description has to contain at least 'type', 'name' and 'ns'";
    }
  },

  /* @private */
  matchQueryParams: function (queryObject, serviceDescription) {
    var me = this,
        match = true,
        queryPropertyName, queryProperty, serviceProperty;

    for (queryPropertyName in queryObject) {
      if (queryObject.hasOwnProperty(queryPropertyName)) {
        queryProperty = queryObject[queryPropertyName];
        serviceProperty = serviceDescription[queryPropertyName];

        match = match && me.checkProperty(queryProperty, serviceProperty);
        if (!match) {
          break;
        }
      }
    }

    return match;
  },

  /* @private */
  checkProperty: function (lookup, serviceProperty) {
    var typeOfServiceProperty = (typeof serviceProperty);

    if ((typeOfServiceProperty === "string") || (typeOfServiceProperty === "number")) {
      return serviceProperty === lookup;
    } else if (Array.isArray(serviceProperty)) {
      return (serviceProperty.indexOf(lookup) > -1);
    }
    return false;
  },

  /* @private */
  buildResult: function (serviceId, serviceDescription, params) {
    var me = this,
        result;

    if (params.summarize) {
      result = {
        id: serviceId,
        name: serviceDescription.name,
        desc: serviceDescription.description
      };
    } else {
      result = {
        id: serviceId,
        serviceDescription: serviceDescription
      };

      if (params.includeMaskFields) {
        result.fields = me.getMaskLines(serviceDescription.masks);
      }

      if (params.evaluateConfig) {
        me.evaluateConfig(serviceDescription);
      }
    }

    return result;
  },

  /* @private */
  getMaskLines: function (masks) {
    var me = this,
        maskLines = [],
        docMask, seen;

    if (masks && (masks.length > 0)) {
      masks.forEach(function (maskName) {
        docMask = me.getDocMask(maskName);
        if (docMask && docMask.fields && (docMask.fields.length > 0)) {
          maskLines = maskLines.concat(docMask.fields);
        }
      });
    }

    seen = {};
    return maskLines.filter(function (item) {
      return seen.hasOwnProperty(item.key) ? false : (seen[item.key] = true);
    });
  },

  /* @private */
  getDocMask: function (name) {
    var me = this,
        docMask, docMaskData;

    if (!me.docMaskCache[name]) {
      docMask = sol.common.SordUtils.getDocMask(name, ixConnect.loginResult.clientInfo.language);
      docMaskData = { fields: [] };
      docMask.lines.forEach(function (docMaskLine) {
        docMaskData.fields.push({
          type: "GRP",
          key: String(docMaskLine.key),
          name: String(docMaskLine.name)
        });
      });
      me.docMaskCache[name] = docMaskData;
    }

    return me.docMaskCache[name];
  },

  /* @private */
  evaluateConfig: function (serviceDescription) {
    var me = this,
        configContentObj, configContentStr;

    if ((serviceDescription.type === me.TYPES.CONFIG) && serviceDescription && serviceDescription.cfg && serviceDescription.cfg.$config) {
      configContentObj = sol.create("sol.common.Config", { compose: serviceDescription.cfg.$config });
      configContentStr = sol.create("sol.common.Template", { source: JSON.stringify(serviceDescription.cfg) }).apply(configContentObj);
      serviceDescription.cfg = JSON.parse(configContentStr);
      delete serviceDescription.cfg.$config;
    }
  }

});