fcf.module({
  name:         "fcf:NSystem/NPackage/configHandlers.js",
  dependencies: [],
  lazy:         ["fcf:NSystem/NPackage/tools.js"],
  module: function(){
    var NPackage = fcf.prepareObject(fcf, "NSystem/NPackage");

    class ConfigHandlers {

      //
      // Elements with programmatic merge
      //
      "packages"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.packages = fcf.append([], a_dstConfig.packages, a_sourceConfig.packages);
      }

      "public"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.public = fcf.append([], a_dstConfig.public, a_sourceConfig.public);
      }

      "packageDirectories"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.packageDirectories = fcf.append([], a_dstConfig.packageDirectories, a_sourceConfig.packageDirectories);
      }

      "projectionDirectories"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.projectionDirectories = fcf.append([], a_dstConfig.projectionDirectories, a_sourceConfig.projectionDirectories);
      }

      "projections"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.projections = fcf.append([], a_dstConfig.projections, a_sourceConfig.projections);
      }

      "aliases"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.aliases = fcf.append({}, a_dstConfig.aliases, a_sourceConfig.aliases);
      }

      "usageViews"(a_dstConfig, a_sourceConfig, a_global) {
        if (!a_dstConfig.usageViews)
          a_dstConfig.usageViews = {};
        fcf.each(a_sourceConfig.usageViews, (a_type, a_usage)=>{
          a_dstConfig.usageViews[a_type] = fcf.append({}, a_dstConfig.usageViews[a_type], a_usage);
        });
      }

      "filters"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.filters = fcf.append({}, a_dstConfig.filters, a_sourceConfig.filters);
      }

      "functions"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.functions = fcf.append({}, a_dstConfig.functions, a_sourceConfig.functions);
      }

      "html"(a_dstConfig, a_sourceConfig, a_global) {
        if (!a_dstConfig.html)
          a_dstConfig.html = { };
        a_dstConfig.html.include = fcf.append({}, a_dstConfig.html.include, a_sourceConfig.html.include);
      }

      "inheritanceModes"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.inheritanceModes = fcf.append({}, a_dstConfig.inheritanceModes, a_sourceConfig.inheritanceModes);
      }

      "routes"(a_dstConfig, a_sourceConfig, a_global) {
        a_dstConfig.routes = fcf.append([], a_dstConfig.routes, a_sourceConfig.routes);
      }

      "dependencies"(a_dstConfig, a_sourceConfig, a_global) {
        if (!a_global)
          a_dstConfig.dependencies = fcf.append([], a_dstConfig.dependencies, a_sourceConfig.dependencies);
      }

      "configFiles"(a_dstConfig, a_sourceConfig) {
      }

      "nodeDependencies"(a_dstConfig, a_sourceConfig){
        if (!a_dstConfig.nodeDependencies)
          a_dstConfig.nodeDependencies = {};

        fcf.each(a_sourceConfig.nodeDependencies, (a_package, a_info)=>{
          if (typeof a_info !== "object")
            a_info = {};

          if (!(a_package in a_dstConfig.nodeDependencies)){
            a_dstConfig.nodeDependencies[a_package] = a_info;
            return;
          }

          let versionStrSrc = typeof a_info == "object" &&
                              a_info.version
                                  ? a_info.version
                                  : "";
          let versionStrDst = typeof a_dstConfig.nodeDependencies[a_package] == "object" &&
                              a_dstConfig.nodeDependencies[a_package].version
                                  ? a_dstConfig.nodeDependencies[a_package].version
                                  : "";
          let cmpVersion            = NPackage.tools.cmpVersion(versionStrDst, versionStrSrc);
          let versionRequirementSrc = NPackage.tools.getVersionRequirement(versionStrSrc);
          let versionRequirementDst = NPackage.tools.getVersionRequirement(versionStrDst);

          if (((versionRequirementDst == ">"  && versionRequirementSrc == "<") ||
               (versionRequirementDst == ">=" && versionRequirementSrc == "<") ||
               (versionRequirementDst == ">"  && versionRequirementSrc == "<=") ||
               (versionRequirementDst == "<"  && versionRequirementSrc == ">") ||
               (versionRequirementDst == "<=" && versionRequirementSrc == ">") ||
               (versionRequirementDst == "<"  && versionRequirementSrc == ">=")
              ) &&
               cmpVersion == 0) {
            throw new fcf.Exception("ERROR", {error: `Incompatible dependencies ${versionStrSrc} and ${versionStrDst} for node package ${a_package}`});
          }

          if ((versionRequirementDst == ">=" || versionRequirementDst == ">" || versionRequirementDst == "=") &&
              (versionRequirementSrc == "<=" || versionRequirementSrc == "<" || versionRequirementSrc == "=") &&
              cmpVersion == 1) {
            throw new fcf.Exception("ERROR", {error: `Incompatible dependencies ${versionStrSrc} and ${versionStrDst} for node package ${a_package}`});
          }

          if ((versionRequirementDst == "<=" || versionRequirementDst == "<" || versionRequirementDst == "=") &&
              (versionRequirementSrc == ">=" || versionRequirementSrc == ">" || versionRequirementSrc == "=") &&
              cmpVersion == -1) {
            throw new fcf.Exception("ERROR", {error: `Incompatible dependencies ${versionStrSrc} and ${versionStrDst} for node package ${a_package}`});
          }

          if (cmpVersion == 0){
            return;
          }

          if (versionRequirementSrc == ""){
            return;
          }

          if (versionRequirementDst == "="){
            return;
          }

          if (versionRequirementDst == ""){
            a_dstConfig.nodeDependencies[a_package] = a_info;
            return;
          }

          if (versionRequirementSrc == "="){
            a_dstConfig.nodeDependencies[a_package] = a_info;
            return;
          }

          if ((versionRequirementDst == "<=" || versionRequirementDst == "<") &&
              (versionRequirementSrc == "<=" || versionRequirementSrc == "<")){
            if (cmpVersion == 1){
              a_dstConfig.nodeDependencies[a_package] = a_info;
              return;
            }
          }

          if ((versionRequirementDst == ">=" || versionRequirementDst == ">") &&
              (versionRequirementSrc == ">=" || versionRequirementSrc == ">")){
            if (cmpVersion == -1){
              a_dstConfig.nodeDependencies[a_package] = a_info;
              return;
            }
          }

          if (cmpVersion == -1)
            a_dstConfig.nodeDependencies[a_package] = a_info;
        });
      }

      //
      // other parameters
      //
      "*"(a_dstConfig, a_source, a_itemKey, a_global){
        a_dstConfig[a_itemKey] = a_source[a_itemKey];
      }
    };

    NPackage.configHandlers = new ConfigHandlers();

    return NPackage.configHandlers;
  }
});
