fcf.module({
  name: "fcf:NFSQL/NDetails/Tools.js",
  dependencies: [],
  module: function() {
    var Tools = fcf.prepareObject(fcf, "NFSQL.NDetails.Tools");

    Tools.eachProjection = function(a_taskInfo, a_cb){
      a_cb({part: "from", block: a_taskInfo.query, key: "from", projection: a_taskInfo.query.from});
      fcf.each(a_taskInfo.query.join, (a_key, a_join) => {
        a_cb({part: "join", block: a_join, key: "from", projection: a_join.from});
      });
    }

    Tools.eachFieldBlockWhere = function(a_taskInfo, a_blockWhere, a_cb){
      let part = "where";
      fcf.each(a_blockWhere, function(a_key, a_item){
        if (a_item.type != "block"){
          fcf.each(a_item.args, function(a_subkey, a_subitem){
            if (a_subitem.field)
              a_cb({part: part, field: a_subitem});
            else if (a_item.function)
              Tools._eachFieldForFunction(a_taskInfo, a_subitem, part, a_cb);
          });
        } else {
          Tools.eachFieldBlockWhere(a_taskInfo, a_item.args, a_cb);
        }
      });
    }

    Tools.eachReplacementBlockWhere = function(a_taskInfo, a_blockWhere, a_cb){
      let part = "where";
      fcf.each(a_blockWhere, function(a_key, a_item){
        if (a_item.type != "block"){
          fcf.each(a_item.args, function(a_subkey, a_subitem){
            if ("result" in a_subitem)
              a_cb({part: part, field: a_subitem});
            else if (a_item.function)
              Tools._eachReplacementForFunction(a_taskInfo, a_subitem, part, a_cb);
          });
        } else {
          Tools.eachReplacementBlockWhere(a_taskInfo, a_item.args, a_cb);
        }
      });
    }

    Tools.eachField = function(a_taskInfo, a_query, a_cb){
      if (a_query.type == "select") {
        let part = "fields";
        let join = fcf.append([], a_query.join);
        let diff = 0;
        let lastLength = a_query.fields ? a_query.fields.length : 0;
        fcf.each(fcf.append([], a_query.fields), (a_key, a_item) => {
          if (a_item.field) {
            a_key -= diff;
            a_cb({part: part, key: a_key, field: a_item, object: a_query.fields});
            if (lastLength > a_query.fields.length)
              diff += lastLength - a_query.fields.length;
            lastLength = a_query.fields.length;
          } else if (a_item.function) {
            Tools._eachFieldForFunction(a_taskInfo, a_item, part, a_cb);
          }
        })
        fcf.each(join, (a_key, a_join) => {
          Tools.eachFieldBlockWhere(a_taskInfo, a_join.on, a_cb)
        })
        Tools.eachFieldBlockWhere(a_taskInfo, a_query.where, a_cb)
      } else if (a_query.type == "update") {
        let part = "fields";
        let diff = 0;
        let lastLength = a_query.values ? a_query.values.length : 0;
        fcf.each(fcf.append([], a_query.values), (a_key, a_item) => {
          a_key -= diff;
          if (a_item.field)
            a_cb({part: part, key: a_key, field: a_item});
          if (lastLength > a_query.values.length)
            diff += lastLength - a_query.values.length;
          lastLength = a_query.values.length;
        })

        fcf.each(a_query.join, (a_key, a_join) => {
          Tools.eachFieldBlockWhere(a_taskInfo, a_join.on, a_cb)
        })

        Tools.eachFieldBlockWhere(a_taskInfo, a_query.where, a_cb)
      } else if (a_query.type == "insert") {
        let part = "fields";
        let diff = 0;
        let lastLength = a_query.values ? a_query.values.length : 0;
        fcf.each(fcf.append([], a_query.values), (a_key, a_item) => {
          a_key -= diff;
          if (a_item.field)
            a_cb({part: part, key: a_key, field: a_item});
          if (lastLength > a_query.values.length)
            diff += lastLength - a_query.values.length;
          lastLength = a_query.values.length;
        })
      } else if (a_query.type == "delete") {
        let part = "fields";
        let join = fcf.append([], a_query.join);
        fcf.each(join, (a_key, a_join) => {
          Tools.eachFieldBlockWhere(a_taskInfo, a_join.on, a_cb)
        })
        Tools.eachFieldBlockWhere(a_taskInfo, a_query.where, a_cb)
      }
    }

    Tools.eachReplacement = function(a_taskInfo, a_query, a_cb){
      if (a_query.type == "select") {
        let part = "fields";
        let join = fcf.append([], a_query.join);
        fcf.each(fcf.append([], a_query.fields), (a_key, a_item) => {
          if (!fcf.empty(a_item.result))
            a_cb({part: part, key: a_key, field: a_item});
          else if (a_item.function)
            Tools._eachReplacementForFunction(a_taskInfo, a_item, part, a_cb);
        })

        fcf.each(join, (a_key, a_join) => {
          Tools.eachReplacementBlockWhere(a_taskInfo, a_join.on, a_cb)
        })
        Tools.eachReplacementBlockWhere(a_taskInfo, a_query.where, a_cb)
      } else if (a_query.type == "update") {
        let part = "fields";
        fcf.each(fcf.append([], a_query.values), (a_key, a_item) => {
          if (!fcf.empty(a_item.result))
            a_cb({part: part, key: a_key, field: a_item});
        })

        fcf.each(a_query.join, (a_key, a_join) => {
          Tools.eachReplacementBlockWhere(a_taskInfo, a_join.on, a_cb)
        })

        Tools.eachReplacementBlockWhere(a_taskInfo, a_query.where, a_cb)
      } else if (a_query.type == "insert") {
        let part = "fields";
        fcf.each(fcf.append([], a_query.values), (a_key, a_item) => {
          if (!fcf.empty(a_item.result))
            a_cb({part: part, key: a_key, field: a_item});
        })
      }
    }


    Tools.eachFunctionBlockWhere = function(a_taskInfo, a_blockWhere, a_cb){
      let part = "where";
      fcf.each(a_blockWhere, function(a_key, a_item){
        if (a_item.type != "block"){
          fcf.each(a_item.args, function(a_subkey, a_subitem){
            if (a_subitem.function){
              Tools._eachFunctionsArgs(a_taskInfo, a_subitem.args, part, a_cb);
              a_cb({part: part, function: a_subitem});
            }
          });
        } else {
          Tools.eachFunctionBlockWhere(a_taskInfo, a_item.args, a_cb);
        }
      });
    }

    Tools.eachFunctions = function(a_taskInfo, a_query, a_cb){
      if (a_query.type == "select") {
        let part = "fields";
        fcf.each(a_query.fields, (a_key, a_item) => {
          if (!a_item.function)
            return;
          Tools._eachFunctionsArgs(a_taskInfo, a_item.args, part, a_cb);
          a_cb({part: part, function: a_item});
        })

        fcf.each(a_query.join, (a_key, a_join) => {
          Tools.eachFunctionBlockWhere(a_taskInfo, a_join.on, a_cb)
        })

        Tools.eachFunctionBlockWhere(a_taskInfo, a_query.where, a_cb)
      }
      else if (a_query.type == "update") {
        Tools.eachFunctionBlockWhere(a_taskInfo, a_query.where, a_cb)
      }
      else if (a_query.type == "delete") {
        Tools.eachFunctionBlockWhere(a_taskInfo, a_query.where, a_cb)
      }
    }

    Tools._eachFieldForFunction = function(a_taskInfo, a_item, a_part, a_cb){
      fcf.each(a_item.args, (a_key, a_arg) => {
        if (a_arg.function) {
          Tools._eachFieldForFunction(a_taskInfo, a_arg, a_part, a_cb)
        } else if (a_arg.field) {
          a_cb({part: a_part, field: a_arg});
        }
      })
    }

    Tools._eachReplacementForFunction = function(a_taskInfo, a_item, a_part, a_cb){
      fcf.each(a_item.args, (a_key, a_arg) => {
        if (a_arg.function) {
          Tools._eachFieldForFunction(a_taskInfo, a_arg, a_part, a_cb)
        } else if (a_arg.value && !fcf.empty(a_arg.value.result)) {
          a_cb({part: a_part, field: a_arg});
        }
      })
    }

    Tools._eachFunctionsArgs = function(a_taskInfo, a_args, a_part, a_cb){
      fcf.each(a_args, (a_key, a_item) => {
        if (!a_item.function)
          return;
        a_cb({part: a_part, function: a_item});
        Tools._eachFunctionsArgs(a_taskInfo, a_item.args, a_part, a_cb)
      })
    }

    return Tools;
  }
});
