package com.alibaba.druid.sql.dialect.mongo.parser;

import com.alibaba.druid.sql.dialect.mongo.ast.JsLexer;
import com.alibaba.druid.sql.parser.ParserException;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import java.util.List;
import java.util.Stack;
import util.sqlparse.visitor.mongo.Identifier;

public class ApiTreeParser {
   private String api;
   private String collection;
   private Method method;

   public ApiTreeParser(String api) {
      this.api = api;
      this.method = new Method();
   }

   public void parse() {
      CharStream stream = CharStreams.fromString(this.api);
      JsLexer lexer = new JsLexer(stream);
      CommonTokenStream tokenStream = new CommonTokenStream(lexer);
      tokenStream.fill();
      List<Token> tokens = tokenStream.getTokens();

      for(int i = 0; i < tokens.size(); ++i) {
         System.out.println(i + " " + ((Token)tokens.get(i)).getType() + "\t" + ((Token)tokens.get(i)).getText() + "");
      }

      if (Identifier.Db.code.equals(((Token)tokens.get(0)).getText()) && Identifier.Dot.code.equals(((Token)tokens.get(1)).getText()) && 6 == ((Token)tokens.get(2)).getType() || Identifier.Db.code.equals(((Token)tokens.get(0)).getText()) && 15 == ((Token)tokens.get(1)).getType() && 7 == ((Token)tokens.get(2)).getType()) {
         int index = 2;
         if (11 == ((Token)tokens.get(3)).getType()) {
            index = 2;
         } else if (15 == ((Token)tokens.get(1)).getType()) {
            this.collection = ((Token)tokens.get(2)).getText();
            index = 5;
         } else {
            this.collection = ((Token)tokens.get(2)).getText();
            index = 4;
         }

         boolean flag = this.parseMethod(tokens, index, this.getMethod());
         if (!flag) {
            throw new ParserException("syntax error. ");
         }
      } else {
         throw new ParserException("syntax error. ");
      }
   }

   private boolean parseMethod(List<Token> tokens, int index, Method method) {
      boolean flag = false;
      method.setName(((Token)tokens.get(index)).getText());
      int lparen = index + 1;
      int loop = lparen + 1;
      Stack parenStack = new Stack();
      Stack bparenStack = new Stack();
      Stack squareStack = new Stack();
      parenStack.push(11);
      boolean isFucntion = false;
      int startIndex = 0;
      int endIndex = 0;

      while(true) {
         if (11 == ((Token)tokens.get(loop)).getType()) {
            parenStack.push(11);
         } else if (12 == ((Token)tokens.get(loop)).getType()) {
            parenStack.pop();
            if (parenStack.empty()) {
               flag = true;
               ++loop;
               break;
            }
         } else if (!isFucntion && squareStack.isEmpty() && 13 == ((Token)tokens.get(loop)).getType()) {
            if (bparenStack.empty()) {
               startIndex = loop;
            }

            bparenStack.push(13);
         } else if (!isFucntion && squareStack.isEmpty() && 14 == ((Token)tokens.get(loop)).getType()) {
            bparenStack.pop();
            if (bparenStack.empty()) {
               this.buildMethodArg(tokens, startIndex, loop, method);
            }
         } else if (bparenStack.isEmpty() && 15 == ((Token)tokens.get(loop)).getType()) {
            if (squareStack.empty()) {
               startIndex = loop;
            }

            squareStack.push(15);
         } else if (bparenStack.isEmpty() && 16 == ((Token)tokens.get(loop)).getType()) {
            squareStack.pop();
            if (squareStack.empty()) {
               this.buildMethodArg(tokens, startIndex, loop, method);
            }
         } else if (squareStack.isEmpty() && bparenStack.empty() && 6 == ((Token)tokens.get(loop)).getType() && "function".equalsIgnoreCase(((Token)tokens.get(loop)).getText())) {
            startIndex = loop;
            isFucntion = true;
         } else if (isFucntion && squareStack.isEmpty() && 13 == ((Token)tokens.get(loop)).getType()) {
            bparenStack.push(13);
         } else if (isFucntion && squareStack.isEmpty() && 14 == ((Token)tokens.get(loop)).getType()) {
            bparenStack.pop();
            if (bparenStack.empty()) {
               this.buildMethodArg(tokens, startIndex, loop, method);
               isFucntion = false;
            }
         } else if (!isFucntion && squareStack.empty() && bparenStack.empty() && (6 == ((Token)tokens.get(loop)).getType() || 7 == ((Token)tokens.get(loop)).getType() || 9 == ((Token)tokens.get(loop)).getType() || 3 == ((Token)tokens.get(loop)).getType() || 4 == ((Token)tokens.get(loop)).getType() || 5 == ((Token)tokens.get(loop)).getType())) {
            method.addArg(((Token)tokens.get(loop)).getText(), ((Token)tokens.get(loop)).getType());
         }

         if (-1 == ((Token)tokens.get(loop)).getType()) {
            break;
         }

         ++loop;
      }

      if (8 == ((Token)tokens.get(loop)).getType()) {
         ++loop;
         method.setNext(new Method());
         flag = this.parseMethod(tokens, loop, method.getNext());
      }

      return flag;
   }

   private void buildMethodArg(List<Token> tokens, int startIndex, int endIndex, Method method) {
      StringBuilder builder = new StringBuilder();

      for(int i = startIndex; i <= endIndex; ++i) {
         builder.append(((Token)tokens.get(i)).getText());
      }

      method.addArg(builder.toString(), ((Token)tokens.get(startIndex)).getType());
   }

   public String getApi() {
      return this.api;
   }

   public void setApi(String api) {
      this.api = api;
   }

   public String getCollection() {
      return this.collection;
   }

   public void setCollection(String collection) {
      this.collection = collection;
   }

   public Method getMethod() {
      return this.method;
   }

   public void setMethod(Method method) {
      this.method = method;
   }
}
