package util.sqlparse.visitor.mysql.visitor;

import bean.DataBase;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
import com.alibaba.druid.sql.ast.statement.SQLAlterFunctionStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterIndexStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterViewStatement;
import com.alibaba.druid.sql.ast.statement.SQLBlockStatement;
import com.alibaba.druid.sql.ast.statement.SQLCallStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateIndexStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropFunctionStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropIndexStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTriggerStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropViewStatement;
import com.alibaba.druid.sql.ast.statement.SQLGrantStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLReplaceStatement;
import com.alibaba.druid.sql.ast.statement.SQLReturnStatement;
import com.alibaba.druid.sql.ast.statement.SQLRevokeStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlSelectIntoStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterEventStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateEventStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadXmlStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.DefaultMySqlASTVisitor;
import java.util.List;
import util.sqlparse.visitor.common.Context;
import util.sqlparse.visitor.common.bean.SQLResult;
import util.sqlparse.visitor.common.bean.StatementType;
import util.sqlparse.visitor.common.scope.Scope;
import util.sqlparse.visitor.mysql.SQLParser;

public class CompoundVisitor extends DefaultMySqlASTVisitor {
   private boolean debug = false;
   private Scope scope;

   public CompoundVisitor(Scope scope) {
      this.scope = scope;
   }

   public void perform(List<SQLStatement> statements) {
      if (statements != null && statements.size() != 0) {
         for(SQLStatement statement : statements) {
            statement.accept(this);
         }

         this.scope.reset();
      }
   }

   public boolean visit(SQLCreateProcedureStatement statement) {
      SQLStatement block = statement.getBlock();
      if (block == null) {
         return false;
      } else {
         block.accept(this);
         return false;
      }
   }

   public boolean visit(SQLCreateFunctionStatement statement) {
      SQLStatement block = statement.getBlock();
      if (block == null) {
         return false;
      } else {
         block.accept(this);
         return false;
      }
   }

   public boolean visit(SQLCreateTriggerStatement statement) {
      SQLStatement block = statement.getBody();
      if (block == null) {
         return false;
      } else {
         block.accept(this);
         return false;
      }
   }

   public boolean visit(MySqlCreateEventStatement statement) {
      SQLStatement block = statement.getEventBody();
      if (block == null) {
         return true;
      } else {
         this.parseStatement(block);
         return false;
      }
   }

   public boolean visit(MySqlAlterEventStatement statement) {
      SQLStatement block = statement.getEventBody();
      if (block == null) {
         return false;
      } else {
         this.parseStatement(block);
         return false;
      }
   }

   public boolean visit(SQLBlockStatement statement) {
      for(SQLStatement sqlStatement : statement.getStatementList()) {
         sqlStatement.accept(this);
      }

      return false;
   }

   public boolean visit(SQLSelectStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlSelectIntoStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLInsertStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlInsertStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLReturnStatement statement) {
      SQLExpr expr = statement.getExpr();
      if (expr instanceof SQLQueryExpr) {
         SQLQueryExpr queryExpr = (SQLQueryExpr)expr;
         SQLSelectStatement select = new SQLSelectStatement();
         select.setSelect(queryExpr.subQuery);
         select.accept(this);
      }

      return false;
   }

   public boolean visit(SQLUpdateStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlUpdateStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDeleteStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlDeleteStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLReplaceStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLCreateTableStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLAlterTableStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropTableStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLCreateIndexStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLAlterIndexStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropIndexStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLCreateViewStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLAlterViewStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropViewStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlLoadXmlStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlLoadDataInFileStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(MySqlRenameTableStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLAlterProcedureStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropProcedureStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLAlterFunctionStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropFunctionStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLDropTriggerStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLCallStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLGrantStatement statement) {
      return this.parseStatement(statement);
   }

   public boolean visit(SQLRevokeStatement statement) {
      return this.parseStatement(statement);
   }

   private boolean parseStatement(SQLStatement statement) {
      if (this.debug) {
         System.out.println(statement);
      }

      StatementType type = SQLParser.getStatementType(statement);
      switch (type) {
         case select:
         case update:
         case delete:
         case insert:
         case replace:
         case loadData:
         case loadXml:
         case truncate:
         case renameTable:
         case createTable:
         case alterTable:
         case dropTable:
         case createIndex:
         case alterIndex:
         case dropIndex:
         case createView:
         case alterView:
         case dropView:
         case createProc:
         case alterProc:
         case dropProc:
         case createFunction:
         case alterFunction:
         case dropFunction:
         case createTrigger:
         case dropTrigger:
         case createEvent:
         case alterEvent:
         case dropEvent:
         case revoke:
         case grant:
            this.parseResult(statement);
            return false;
         case callProc:
            statement.accept(this);
            break;
         default:
            statement.accept(this);
      }

      return true;
   }

   private void parseResult(SQLStatement statement) {
      SQLParser parser = new SQLParser();
      Context context = this.scope.context;
      DataBase dataBase = context.getDataBase();
      String schema = context.getSchema();
      SQLResult result = parser.parse(statement, dataBase, schema);
      this.scope.results.add(result);
   }
}
