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

import com.chenyang.druid.sql.ast.SQLExpr;
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.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 AdsCreateTableParser extends SQLCreateTableParser {
   public AdsCreateTableParser(String sql) {
      super(sql);
   }

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

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

         this.accept(Token.CREATE);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.DIMENSION)) {
         this.lexer.nextToken();
         stmt.setDimension(true);
      }

      this.accept(Token.TABLE);
      if (this.lexer.token() == Token.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());
                  }

                  if (this.lexer.token() != Token.INDEX) {
                     SQLColumnDefinition column = this.exprParser.parseColumn();
                     stmt.getTableElementList().add(column);
                  } else {
                     this.lexer.nextToken();
                     this.accept(Token.IDENTIFIER);
                     this.accept(Token.IDENTIFIER);
                     this.accept(Token.LPAREN);
                     this.accept(Token.IDENTIFIER);

                     while(this.lexer.token() == Token.COMMA) {
                        this.accept(Token.IDENTIFIER);
                     }

                     this.accept(Token.RPAREN);
                  }
               } 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.AS) {
         this.lexer.nextToken();
         SQLSelect select = this.createSQLSelectParser().select();
         stmt.setSelect(select);
      }

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

      if (this.lexer.identifierEquals("PARTITION")) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         this.acceptIdentifier("HASH");
         this.accept(Token.KEY);
         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);
               this.acceptIdentifier("PARTITION");
               this.acceptIdentifier("NUM");
               this.accept(Token.LITERAL_INT);
               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.TABLEGROUP)) {
         this.lexer.nextToken();
         this.accept(Token.IDENTIFIER);
      }

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

         while(true) {
            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) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.token() == Token.COMMENT) {
         this.lexer.nextToken();
         this.accept(Token.LITERAL_CHARS);
      }

      return stmt;
   }
}
