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

    fcf.addException("ERROR_NFSQL_PARSE_UNKNOWN_COMMAND", "Unknown command ${{1}}$");
    fcf.addException("ERROR_NFSQL_INCORRECT_SINTAXIS", "Format error in query \"${{1}}$\" on position ${{2}}$");

    var stBuffer = new Map();
    var stBufferArr = [];

    NDetails.SingleParser = function() {

      this.parse = function(a_fsql, a_args) {
        a_args = a_args ? a_args : [];

        while(stBuffer.size > fcf.application.getConfiguration().maxFSQLBufferQuerySize){
          stBuffer.delete(stBufferArr[stBufferArr.length-1]);
          stBufferArr.pop();
        }

        for (let i = 0; i < a_args.length; ++i) {
          if (typeof a_args[i] !== "string")
            continue;
          let intVal = parseInt(a_args[i]);
          if (!isNaN(intVal) && intVal.toString().length == a_args[i].length)
            a_args[i] = intVal;
        }

        if (stBuffer.has(a_fsql)){
          let query = fcf.append(true, {}, stBuffer.get(a_fsql));
          for(let path in query.args){
            let ref = fcf.resolveEx(query, path, true);
            ref.object[ref.key] = a_args[parseInt(query.args[path])-1];
          }
          return query;
        }

        var state = new Automate.State();

        state.step = new Automate.StepStart(state);
        state.details = {};
        state.details.args = a_args;

        state.count = 0;
        state.content = a_fsql;

        var fakedata = false;
        try {
          for (; state.count < a_fsql.length; ++state.count) {
            state.step.feed(a_fsql.charAt(state.count));
          }
          state.step.feed("\n");
          state.step.actionSwitch();
        } catch(e){
          var errTxt = "<b>";
          errTxt += a_fsql.substr(0, state.count);
          errTxt += "</b>";
          errTxt += a_fsql.substr(state.count);
          throw new fcf.Exception("ERROR_NFSQL_INCORRECT_SINTAXIS", [errTxt, state.count], e);
        }

        var result = {};
        for(var k in state.step.state) {
          if (typeof state.step.state[k] === "function")
            continue;
          result[k] = state.step.state[k];
        }

        delete result.content;
        delete result.count;
        delete result.step;
        delete result.buffer;
        delete result.details;

        stBuffer.set(a_fsql, fcf.append(true, {}, result));
        stBufferArr.push(a_fsql);

        return result;

      }

      this.parseWhere = function(a_fsql, a_args) {
        a_args = a_args ? a_args : [];

        for (let i = 0; i < a_args.length; ++i) {
          if (typeof a_args[i] !== "string")
            continue;
          let intVal = parseInt(a_args[i]);
          if (!isNaN(intVal) && intVal.toString().length == a_args[i].length)
            a_args[i] = intVal;
        }


        var state = new Automate.State();

        state.step = new Automate.StepFirstWhereStart(state);
        state.details = {};
        state.details.args = a_args;

        state.count = 0;
        state.content = a_fsql;

        var fakedata = false;
        try {
          for (; state.count < a_fsql.length; ++state.count) {
            state.step.feed(a_fsql.charAt(state.count));
          }
          state.step.feed("\n");
          state.step.actionSwitch();
        } catch(e){
          var errTxt = "<b>";
          errTxt += a_fsql.substr(0, state.count);
          errTxt += "</b>";
          errTxt += a_fsql.substr(state.count);
          throw new fcf.Exception("ERROR_NFSQL_INCORRECT_SINTAXIS", [errTxt, state.count], e);
        }

        var result = {};
        for(var k in state.step.state) {
          if (typeof state.step.state[k] === "function")
            continue;
          result[k] = state.step.state[k];
        }

        return result.where;

      }

    }

    return NDetails.SingleParser;
  }
});
