package com.alibaba.druid.sql.transform;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExprGroup;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import com.alibaba.druid.sql.repository.SchemaRepository;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SQLUnifiedVisitor extends MySqlASTVisitorAdapter {
   public boolean visit(MySqlSelectQueryBlock x) {
      SchemaRepository repository = new SchemaRepository(DbType.mysql);
      repository.resolve((SQLSelectQueryBlock)x);
      List<SQLSelectItem> selectList = x.getSelectList();

      for(SQLSelectItem item : selectList) {
         if (item.getExpr() instanceof SQLPropertyExpr) {
            SQLPropertyExpr expr = (SQLPropertyExpr)item.getExpr();
            SQLTableSource resolvedTableSource = expr.getResolvedTableSource();
            if (resolvedTableSource != null) {
               String alias = resolvedTableSource.getAlias();
               if (alias != null) {
                  expr.setOwner(alias);
               } else {
                  expr.setOwner(resolvedTableSource.computeAlias());
               }
            }
         }

         item.setAlias(null);
      }

      Collections.sort(selectList, new Comparator<SQLSelectItem>() {
         public int compare(SQLSelectItem o1, SQLSelectItem o2) {
            return o1.toString().compareToIgnoreCase(o2.toString());
         }
      });
      if (x.getFrom() != null) {
         x.getFrom().accept(this);
      }

      if (x.getWhere() != null) {
         x.getWhere().accept(this);
      }

      if (x.getGroupBy() != null) {
         x.getGroupBy().accept(this);
      }

      return false;
   }

   public boolean visit(SQLBinaryOpExpr x) {
      SQLBinaryOperator operator = x.getOperator();
      if (operator != SQLBinaryOperator.BooleanOr && operator != SQLBinaryOperator.BooleanAnd) {
         SQLExpr left = x.getLeft();
         left.accept(this);
         SQLExpr right = x.getRight();
         right.accept(this);
      } else {
         SQLExpr left = x.getLeft();
         left.accept(this);
         SQLExpr right = x.getRight();
         right.accept(this);
         int compareResult = left.toString().compareToIgnoreCase(right.toString());
         if (compareResult > 0) {
            x.setLeft(right);
            x.setRight(left);
         }
      }

      return false;
   }

   public boolean visit(SQLPropertyExpr x) {
      SQLExpr owner = x.getOwner();
      SQLTableSource resolvedTableSource = x.getResolvedTableSource();
      if (resolvedTableSource != null) {
         String alias = resolvedTableSource.getAlias();
         if (alias != null) {
            x.setOwner(alias);
         } else {
            x.setOwner(resolvedTableSource.computeAlias());
         }
      }

      return false;
   }

   public boolean visit(SQLBinaryOpExprGroup x) {
      Collections.sort(x.getItems(), new Comparator<SQLExpr>() {
         public int compare(SQLExpr o1, SQLExpr o2) {
            return o1.toString().compareToIgnoreCase(o2.toString());
         }
      });
      return false;
   }

   public boolean visit(SQLUnionQuery x) {
      SQLSelectQuery left = x.getLeft();
      SQLSelectQuery right = x.getRight();
      left.accept(this);
      right.accept(this);
      int compareResult = left.toString().compareToIgnoreCase(right.toString());
      if (compareResult > 0) {
         x.setLeft(right);
         x.setRight(left);
      }

      return false;
   }

   public boolean visit(SQLExprTableSource x) {
      String tablename = ((SQLName)x.getExpr()).getSimpleName();
      x.setAlias(tablename);
      return false;
   }

   public boolean visit(SQLSelectGroupByClause x) {
      Collections.sort(x.getItems(), new Comparator<SQLExpr>() {
         public int compare(SQLExpr o1, SQLExpr o2) {
            return o1.toString().compareToIgnoreCase(o2.toString());
         }
      });
      SQLExpr having = x.getHaving();
      if (having != null) {
         having.accept(this);
      }

      return false;
   }
}
