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

    Automate.getWhereBlocks = function(a_state){
      return a_state.details.whereStack.length ? a_state.details.whereStack[a_state.details.whereStack.length-1].args :
             a_state.details.whereContainer    ? a_state.details.whereContainer :
                                                 a_state.where;
    }

    Automate.getWhereBlock = function(a_state){
      return a_state.details.whereStack[a_state.details.whereStack.length-1];
    }

    Automate.pushWhereBlock = function(a_state, a_block) {
      var blocks = Automate.getWhereBlocks(a_state);
      blocks.push(a_block);
      a_state.details.whereStack.push(a_block);
    }

    Automate.popWhereBlock = function(a_state){
      a_state.details.whereStack.pop();
    }

    Automate.getWherePath = function(a_state, a_appendLevel){
      let result = a_state.details.wherePath ? a_state.details.wherePath : "where";
      let firstBlocks = a_state.details.whereContainer ? a_state.details.whereContainer :
                                                         a_state.where;
      result += `[${firstBlocks.length ? firstBlocks.length - 1 : 0}]`;
      for (let i = 0; i < a_state.details.whereStack.length; ++i){
        if (i == a_state.details.whereStack.length-1)
          result += `.args[${a_state.details.whereStack[i].args ? a_state.details.whereStack[i].args.length : 0}]`;
        else
          result += `.args[${a_state.details.whereStack[i].args ? a_state.details.whereStack[i].args.length-1 : 0}]`;
      }
      if (a_appendLevel)
        result += ".args[0]";
      return result;
    }

    Automate.setWhereOptions = function(a_state, a_options){
      a_state.details.whereExOptions = fcf.append(
        {
          exitMap: {
            "where" :    { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true},
            "group" :    { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true},
            "language" : { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true},
            "left" :     { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true},
            "right" :    { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true},
            "join" :     { factory: Automate.StepSelect_InputCommands, name:  "StepSelect_InputCommands", skipFirst: false, attachBuffer: true}
          },
        },
        a_options);
    }

    Automate.getWhereOptions = function(a_state){
      return a_state.details.whereExOptions;
    }

    Automate.StepWhere = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""]     = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};

      var stepInfo = a_state.details.stepInfo;
      Automate.setWhereOptions(a_state, stepInfo);

      this.actionSwitch = function() {
        this.state.details.whereStack = [];
      }

    }

    Automate.StepWhere_Start = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""]   = { factory: Automate.StepWhere_PreReadArg1, name:  "StepWhere_PreReadArg1", skipFirst: false};
      this.map[")"]  = { factory: Automate.StepWhere_CloseBlock, name:  "StepWhere_CloseBlock", skipFirst: true};
    }

    Automate.StepWhere_PreReadArg1 = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{nm}"] = null;
      this.map[""] = {
        map: {
          "" :    { factory: Automate.StepWhere_ReadArg1, name:  "StepWhere_ReadArg1", skipFirst: false, attachBuffer: true},
          "and" : { factory: Automate.StepWhere_AndPost, name:  "StepWhere_AndPost", skipFirst: false},
          "or" :  { factory: Automate.StepWhere_OrPost, name:  "StepWhere_OrPost", skipFirst: false},
          "not" :   { factory: "StepWhere_Arg1Not", skipFirst: false},
          "limit" : { factory: Automate.StepSelect_PostWhere, name:  "StepSelect_PostWhere", skipFirst: false, attachBuffer: true},
          "order" : { factory: Automate.StepSelect_PostWhere, name:  "StepSelect_PostWhere", skipFirst: false, attachBuffer: true},
        }
      };
      fcf.append(this.map[""].map, Automate.getWhereOptions(a_state).exitMap);
    }

    Automate.StepWhere_Arg1Not = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""] = { factory: "StepWhere_ReadArg1", skipFirst: false};

      this.actionSwitch = function() {
       var block = Automate.getWhereBlock(this.state);
       if (!block || !!block.type) {
         block = {logic: "and"};
         Automate.pushWhereBlock(this.state, block);
       }
       block.not  = true;
      }
    }


    Automate.StepWhere_AndPost = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""] = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};

      this.actionSwitch = function() {
        Automate.pushWhereBlock(this.state, {logic: "and"});
      }
    }

    Automate.StepWhere_OrPost = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""] = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};

      this.actionSwitch = function() {
        Automate.pushWhereBlock(this.state, {logic: "or"});
      }
    }

    Automate.StepWhere_ReadArg1 = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""]     = { factory: "StepWhere_ReadArg1Switch", skipFirst: false, attachBuffer: true};
      this.map["("]    = { factory: Automate.StepWhere_OpenBlock, name:  "StepWhere_OpenBlock", skipFirst: true};
    }

    Automate.StepWhere_ReadArg1Switch = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.fields = [];
      let block = Automate.getWhereBlock(this.state);
      let createPathBlockInPath = !block || !!block.type;
      this.map[""] = {  factory:    "StepFieldEx",
                        skipFirst:  false,
                        attachBuffer:  true,
                        enableAs:   false,
                        enableList: false,
                        enableAll:  false,
                        enableArgs:  true,
                        fields:     this.fields,
                        path:       Automate.getWherePath(this.state, createPathBlockInPath),
                        exitMap: {
                          "": { factory: "StepWhere_ReadArg1End", skipFirst: false}
                        }
                     };

      this.actionSwitch = function() {
       var block = Automate.getWhereBlock(this.state);
       if (!block || !!block.type) {
         block = {logic: "and"};
         Automate.pushWhereBlock(this.state, block);
       }
       block.args  = this.fields;
      }
    }

    Automate.StepWhere_ReadArg1End = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.map[""]      = { factory: "StepWhere_WaitArg2", skipFirst: false, attachBuffer: false};
      this.actionSwitch = function() {
       var block = Automate.getWhereBlock(this.state);
      }
    }

    Automate.StepWhere_OpenBlock = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};

      this.actionSwitch = function() {
        var block = Automate.getWhereBlock(this.state);
        if (!block || !!block.type) {
          block = {logic: "and"};
          Automate.pushWhereBlock(this.state, block);
        }
        block.type = "block";
        block.args  = [];
      }
    }

    Automate.StepWhere_CloseBlock = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};

      this.actionSwitch = function() {
        a_state.details.whereStack.pop();
      }
    }


    Automate.StepWhere_WaitArg2 = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      this.map[""]  = { factory: Automate.StepWhere_ReadArg2, name:  "StepWhere_ReadArg2", skipFirst: false};
    }


    Automate.StepWhere_ReadArg2 = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{nm}"] = { factory: Automate.StepWhere_ReadArg2_Cmd, name:  "StepWhere_ReadArg2_Cmd", skipFirst: false};
      this.map["="] = { factory: Automate.StepWhere_ReadCmp, name:  "StepWhere_ReadCmp", skipFirst: false};
      this.map["<"] = { factory: Automate.StepWhere_ReadCmp, name:  "StepWhere_ReadCmp", skipFirst: false};
      this.map[">"] = { factory: Automate.StepWhere_ReadCmp, name:  "StepWhere_ReadCmp", skipFirst: false};
    }

    Automate.StepWhere_ReadArg2_Cmd = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{nm}"] = null;
      this.map["^{nnm}"] = { map: {
        "like":   { factory: "StepWhere_ReadArg2_CmdLike", skipFirst: false},
        "regexp": { factory: "StepWhere_ReadArg2_CmdRegExp", skipFirst: false},
      }};
    }

    Automate.StepWhere_ReadArg2_CmdLike = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_WaitArg3, name:  "StepWhere_WaitArg3", skipFirst: false};
      this.actionSwitch = function() {
        var block = Automate.getWhereBlock(this.state);
        block.type = "like";
      }
    }

    Automate.StepWhere_ReadArg2_CmdRegExp = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_WaitArg3, name:  "StepWhere_WaitArg3", skipFirst: false};
      this.actionSwitch = function() {
        var block = Automate.getWhereBlock(this.state);
        block.type = "regexp";
      }
    }


    Automate.StepWhere_ReadCmp = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_WaitArg3, name:  "StepWhere_WaitArg3", skipFirst: false};
      this.map["="] = null;
      this.map["<"] = null;
      this.map[">"] = null;

      this.actionSwitch = function() {
        var c = this.buffer;
        if (c != "<" && c != ">" && c != "<=" && c != ">=" && c != "=" && c != "<>")
          throw "Unkonown compare operation '" + c + "'";

        var block = Automate.getWhereBlock(this.state);
        block.type = c;
      }
    }

    Automate.StepWhere_WaitArg3 = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map["^{s}"] = null;
      var block = Automate.getWhereBlock(this.state);
      this.map[""] = {  factory:    "StepFieldEx",
                        skipFirst:  false,
                        enableAs:   false,
                        enableList: false,
                        enableAll:  false,
                        enableArgs:  true,
                        fields:     block.args,
                        path:       Automate.getWherePath(this.state),
                        exitMap: {
                          "": { factory: "StepWhere_ReadArg3End", skipFirst: false}
                        }
                      };
    }


    Automate.StepWhere_ReadArg3End = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: "StepWhere_Arg3Close", skipFirst: false};
      this.actionSwitch = function() {
        var block = Automate.getWhereBlock(this.state);
      }
    }


    Automate.StepWhere_Arg3Close = function(a_state) {
      Automate.BaseStep.call(this, a_state);
      this.delimeter = "";
      this.map[""] = { factory: Automate.StepWhere_Start, name:  "StepWhere_Start", skipFirst: false};
      this.actionSwitch = function() {
        a_state.details.whereStack.pop();
      }
    }

    return Automate;
  }
});
