package com.chenyang.druid.sql.dialect.antspark.parser;

import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.expr.SQLListExpr;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLConstraint;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.chenyang.druid.sql.ast.statement.SQLTableElement;
import com.chenyang.druid.sql.dialect.antspark.ast.AntsparkCreateTableStatement;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLCreateTableParser;
import com.chenyang.druid.sql.parser.SQLExprParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.util.FnvHash;

public class AntsparkCreateTableParser extends SQLCreateTableParser {
   public AntsparkCreateTableParser(String sql) {
      super((SQLExprParser)(new AntsparkExprParser(sql)));
   }

   public AntsparkCreateTableParser(SQLExprParser exprParser) {
      super(exprParser);
   }

   public SQLCreateTableStatement parseCreateTable(boolean acceptCreate) {
      AntsparkCreateTableStatement stmt = new AntsparkCreateTableStatement();
      if (acceptCreate) {
         if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
            stmt.addBeforeComment(this.lexer.readAndResetComments());
         }

         this.accept(Token.CREATE);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
         this.lexer.nextToken();
         stmt.setExternal(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY)) {
         this.lexer.nextToken();
         stmt.setType(SQLCreateTableStatement.Type.TEMPORARY);
      }

      this.accept(Token.TABLE);
      if (this.lexer.token() == Token.IF || this.lexer.identifierEquals(FnvHash.Constants.IF)) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         stmt.setIfNotExiists(true);
      }

      stmt.setName(this.exprParser.name());
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();

         do {
            Token token = this.lexer.token();
            if (token != Token.IDENTIFIER && token != Token.LITERAL_ALIAS) {
               if (token != Token.PRIMARY && token != Token.UNIQUE && token != Token.CHECK && token != Token.CONSTRAINT && token != Token.FOREIGN) {
                  if (token == Token.TABLESPACE) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  SQLColumnDefinition column = this.exprParser.parseColumn();
                  stmt.getTableElementList().add(column);
               } else {
                  SQLConstraint constraint = this.exprParser.parseConstaint();
                  constraint.setParent(stmt);
                  stmt.getTableElementList().add((SQLTableElement)constraint);
               }
            } else {
               SQLColumnDefinition column = this.exprParser.parseColumn();
               stmt.getTableElementList().add(column);
            }

            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         } while(this.lexer.token() != Token.RPAREN);

         this.accept(Token.RPAREN);
      }

      if (this.lexer.token() == Token.USING) {
         this.lexer.nextToken();
         SQLName expr = this.exprParser.name();
         stmt.setDatasource(expr);
      }

      if (this.lexer.token() == Token.COMMENT) {
         this.lexer.nextToken();
         SQLExpr comment = this.exprParser.expr();
         stmt.setComment(comment);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.MAPPED)) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         this.exprParser.parseAssignItem(stmt.getMappedBy(), stmt);
      }

      if (this.lexer.token() == Token.PARTITIONED) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         this.accept(Token.LPAREN);

         while(true) {
            if (this.lexer.token() != Token.IDENTIFIER) {
               throw new ParserException("expect identifier. " + this.lexer.info());
            }

            SQLColumnDefinition column = this.exprParser.parseColumn();
            stmt.addPartitionColumn(column);
            if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
               column.addAfterComment(this.lexer.readAndResetComments());
            }

            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
            if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
               column.addAfterComment(this.lexer.readAndResetComments());
            }
         }
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.CLUSTERED)) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         this.accept(Token.LPAREN);

         while(true) {
            SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
            stmt.addClusteredByItem(item);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SKEWED)) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         this.accept(Token.LPAREN);
         this.exprParser.exprList(stmt.getSkewedBy(), stmt);
         this.accept(Token.RPAREN);
         this.accept(Token.ON);
         this.accept(Token.LPAREN);

         while(true) {
            if (this.lexer.token() == Token.LPAREN) {
               SQLListExpr list = new SQLListExpr();
               this.lexer.nextToken();
               this.exprParser.exprList(list.getItems(), list);
               this.accept(Token.RPAREN);
               stmt.addSkewedByOn(list);
            } else {
               SQLExpr expr = this.exprParser.expr();
               stmt.addSkewedByOn(expr);
            }

            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SORTED)) {
         this.parseSortedBy(stmt);
      }

      if (this.lexer.token() == Token.ROW || this.lexer.identifierEquals(FnvHash.Constants.ROW)) {
         this.parseRowFormat(stmt);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SORTED)) {
         this.parseSortedBy(stmt);
      }

      if (stmt.getClusteredBy().size() > 0 || stmt.getSortedBy().size() > 0) {
         this.accept(Token.INTO);
         if (this.lexer.token() != Token.LITERAL_INT) {
            throw new ParserException("into buckets must be integer. " + this.lexer.info());
         }

         stmt.setBuckets(this.lexer.integerValue().intValue());
         this.lexer.nextToken();
         this.acceptIdentifier("BUCKETS");
      }

      if (this.lexer.token() == Token.ROW || this.lexer.identifierEquals(FnvHash.Constants.ROW)) {
         this.parseRowFormat(stmt);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.STORED)) {
         this.lexer.nextToken();
         this.accept(Token.AS);
         if (this.lexer.identifierEquals(FnvHash.Constants.INPUTFORMAT)) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals(FnvHash.Constants.OUTPUTFORMAT)) {
               this.lexer.nextToken();
            }
         } else {
            SQLName name = this.exprParser.name();
            stmt.setStoredAs(name);
         }
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.LOCATION)) {
         this.lexer.nextToken();
         SQLExpr location = this.exprParser.expr();
         stmt.setLocation(location);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.TBLPROPERTIES)) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         do {
            String name = this.lexer.stringVal();
            this.lexer.nextToken();
            this.accept(Token.EQ);
            SQLExpr value = this.exprParser.primary();
            stmt.addOption(name, value);
            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         } while(this.lexer.token() != Token.RPAREN);

         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.META)) {
         this.lexer.nextToken();
         this.acceptIdentifier("LIFECYCLE");
         stmt.setMetaLifeCycle(this.exprParser.primary());
      }

      if (this.lexer.token() == Token.AS) {
         this.lexer.nextToken();
         SQLSelect select = this.createSQLSelectParser().select();
         stmt.setSelect(select);
      }

      if (this.lexer.token() == Token.LIKE) {
         this.lexer.nextToken();
         SQLName name = this.exprParser.name();
         stmt.setLike(name);
      }

      if (this.lexer.token() == Token.COMMENT) {
         this.lexer.nextToken();
         SQLExpr comment = this.exprParser.expr();
         stmt.setComment(comment);
      }

      return stmt;
   }

   private void parseRowFormat(AntsparkCreateTableStatement stmt) {
   }

   private void parseSortedBy(AntsparkCreateTableStatement stmt) {
   }
}
