if (fcf.isServer()) {
  var libFS = require("fs");
  var libUtil = require("util");
}

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

    class Tools {
      constructor(){
      }

      mergeConfiguration(a_dst, a_src, a_global) {
        for(let key in a_src){
          if (key in configHandlers){
            configHandlers[key](a_dst, a_src, a_global);
          } else {
            configHandlers["*"](a_dst, a_src, key, a_global);
          }
        }
      }

      async loadConfigFile(a_packageName, a_filePath, a_dstConfiguration){
        if (!a_dstConfiguration)
          a_dstConfiguration = {};
        a_filePath    = fcf.getPath(a_filePath)
        let configRaw = await libUtil.promisify(libFS.readFile)(fcf.getPath(a_filePath), "utf8");
        let config    = undefined;
        try {
          config = fcf.scriptExecutor.parse(configRaw, {}, a_filePath);
        } catch (error) {
          if (fcf.empty(a_packageName))
            throw new fcf.Exception("ERROR_APPLICATION_CONFIGURATION", {file: a_filePath}, error);
          else
            throw new fcf.Exception("ERROR_PACKAGE_CONFIGURATION", {package: a_packageName, file: a_filePath}, error);
        }
        NPackage.tools.mergeConfiguration(a_dstConfiguration, config, false);

        await fcf.each(config.configs, async (a_key, a_configFilePath)=>{
          if (a_configFilePath.indeOf(":")==-1 && a_configFilePath[0] == "/")
            a_configFilePath = !fcf.empty(a_packageName) ? a_packageName + ":" + a_configFilePath : a_configFilePath;
          await NPackage.tools.loadConfigFile(a_packageName, a_configFilePath, a_dstConfiguration);
        })

        libFS.watchFile(fcf.getPath(a_filePath), {interval: 1000}, (a_eventType)=>{
          fcf.application.getEventChannel().send("watch_file", {file: a_filePath});
        });

        return a_dstConfiguration;
      }

      parseVersion(a_str) {
        a_str = fcf.str(a_str);
        let result = [];
        let pos = 0;
        let code0 = "0".charCodeAt(0);
        let code9 = "9".charCodeAt(0);
        for(; pos < a_str.length; ++pos)
          if (a_str.charCodeAt(pos)>=code0 && a_str.charCodeAt(pos)<=code9)
            break;

        let lst = pos;
        for(; pos < (a_str.length+1); ++pos) {
          if (a_str[pos] == "." || (a_str.charCodeAt(pos)<code0 || a_str.charCodeAt(pos)>code9) || isNaN(a_str.charCodeAt(pos))) {
            let value = parseInt(a_str.substring(lst, pos));
            if (isNaN(value))
              return result;
            result.push(value);
            lst = pos + 1;
            if (a_str[pos] != ".")
              break;
          }
        }

        return result;
      }

      getVersionRequirement(a_str){
        a_str       = fcf.trim(fcf.str(a_str));
        let version = NPackage.tools.parseVersion(a_str);
        if (a_str.indexOf("=") == 0)
          return "=";
        if (a_str.indexOf("<=") == 0)
          return "<=";
        if (a_str.indexOf(">=") == 0)
          return ">=";
        return version.length ? "=" : "";
      }

      cmpVersion(a_left, a_right, a_disableLengthDifferences){
        a_left  = Array.isArray(a_left)  ? a_left  : NPackage.tools.parseVersion(a_left);
        a_right = Array.isArray(a_right) ? a_right : NPackage.tools.parseVersion(a_right);

        let size = fcf.min(a_left.length, a_right.length);

        for(let i = 0; i < size; ++i){
          if (a_left[i] > a_right[i])
            return 1;
          if (a_left[i] < a_right[i])
            return -1;
        }

        if (!a_disableLengthDifferences) {
          if (a_left.length > a_right.length)
            return 1;

          if (a_left.length < a_right.length)
            return -1;
        }

        return 0;
      }

    };

    NPackage.tools = new Tools();

    return NPackage.tools;
  }
});
