package com.alibaba.druid.sql.ast.statement;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLDbTypedObject;
import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.SQLObjectImpl;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class SQLUnionQuery extends SQLObjectImpl implements SQLSelectQuery, SQLDbTypedObject {
   private boolean bracket = false;
   private List<SQLSelectQuery> relations = new ArrayList();
   private SQLUnionOperator operator;
   private SQLOrderBy orderBy;
   private SQLLimit limit;
   private DbType dbType;

   public SQLUnionOperator getOperator() {
      return this.operator;
   }

   public void setOperator(SQLUnionOperator operator) {
      this.operator = operator;
   }

   public SQLUnionQuery() {
      this.operator = SQLUnionOperator.UNION;
   }

   public SQLUnionQuery(SQLSelectQuery left, SQLUnionOperator operator, SQLSelectQuery right) {
      this.operator = SQLUnionOperator.UNION;
      this.setLeft(left);
      this.operator = operator;
      this.setRight(right);
   }

   public List<SQLSelectQuery> getRelations() {
      return this.relations;
   }

   public void addRelation(SQLSelectQuery relation) {
      if (relation != null) {
         relation.setParent(this);
      }

      this.relations.add(relation);
   }

   public SQLSelectQuery getLeft() {
      return this.relations.size() == 0 ? null : (SQLSelectQuery)this.relations.get(0);
   }

   public void setLeft(SQLSelectQuery left) {
      if (left != null) {
         left.setParent(this);
      }

      if (this.relations.size() == 0) {
         this.relations.add(left);
      } else {
         if (this.relations.size() > 2) {
            throw new UnsupportedOperationException("multi-union");
         }

         this.relations.set(0, left);
      }

   }

   public SQLSelectQuery getRight() {
      if (this.relations.size() < 2) {
         return null;
      } else if (this.relations.size() == 2) {
         return (SQLSelectQuery)this.relations.get(1);
      } else {
         throw new UnsupportedOperationException("multi-union");
      }
   }

   public void setRight(SQLSelectQuery right) {
      if (right != null) {
         right.setParent(this);
      }

      if (this.relations.size() == 0) {
         this.relations.add(null);
      }

      if (this.relations.size() == 1) {
         this.relations.add(right);
      } else {
         if (this.relations.size() != 2) {
            throw new UnsupportedOperationException("multi-union");
         }

         this.relations.set(1, right);
      }

   }

   public SQLOrderBy getOrderBy() {
      return this.orderBy;
   }

   public void setOrderBy(SQLOrderBy orderBy) {
      if (orderBy != null) {
         orderBy.setParent(this);
      }

      this.orderBy = orderBy;
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         for(SQLSelectQuery relation : this.relations) {
            relation.accept(visitor);
         }

         if (this.orderBy != null) {
            this.orderBy.accept(visitor);
         }

         if (this.limit != null) {
            this.limit.accept(visitor);
         }
      }

      visitor.endVisit(this);
   }

   public SQLLimit getLimit() {
      return this.limit;
   }

   public void setLimit(SQLLimit limit) {
      if (limit != null) {
         limit.setParent(this);
      }

      this.limit = limit;
   }

   public boolean isBracket() {
      return this.bracket;
   }

   public void setBracket(boolean bracket) {
      this.bracket = bracket;
   }

   public SQLUnionQuery clone() {
      SQLUnionQuery x = new SQLUnionQuery();
      x.bracket = this.bracket;

      for(SQLSelectQuery relation : this.relations) {
         SQLSelectQuery r = relation.clone();
         r.setParent(x);
         x.relations.add(r);
      }

      x.operator = this.operator;
      if (this.orderBy != null) {
         x.setOrderBy(this.orderBy.clone());
      }

      if (this.limit != null) {
         x.setLimit(this.limit.clone());
      }

      x.dbType = this.dbType;
      return x;
   }

   public SQLSelectQueryBlock getFirstQueryBlock() {
      SQLSelectQuery left = this.getLeft();
      if (left instanceof SQLSelectQueryBlock) {
         return (SQLSelectQueryBlock)left;
      } else {
         return left instanceof SQLUnionQuery ? ((SQLUnionQuery)left).getFirstQueryBlock() : null;
      }
   }

   public DbType getDbType() {
      return this.dbType;
   }

   public void setDbType(DbType dbType) {
      this.dbType = dbType;
   }

   public boolean replace(SQLSelectQuery cmp, SQLSelectQuery target) {
      for(int i = 0; i < this.relations.size(); ++i) {
         SQLSelectQuery r = (SQLSelectQuery)this.relations.get(i);
         if (r == cmp) {
            if (cmp != null) {
               cmp.setParent(this);
            }

            this.relations.set(i, cmp);
            return true;
         }
      }

      return false;
   }

   public List<SQLSelectQuery> getChildren() {
      boolean bracket = this.bracket && !(this.parent instanceof SQLUnionQueryTableSource);
      if (this.relations.size() > 2) {
         return this.relations;
      } else {
         SQLSelectQuery left = this.getLeft();
         SQLSelectQuery right = this.getRight();
         if (!bracket && left instanceof SQLUnionQuery && ((SQLUnionQuery)left).getOperator() == this.operator && !right.isBracket() && this.orderBy == null) {
            SQLUnionQuery leftUnion = (SQLUnionQuery)left;
            ArrayList<SQLSelectQuery> rights = new ArrayList();
            rights.add(right);

            while(true) {
               SQLSelectQuery leftLeft = leftUnion.getLeft();
               SQLSelectQuery leftRight = leftUnion.getRight();
               if (leftUnion.isBracket() || leftUnion.getOrderBy() != null || leftLeft.isBracket() || leftRight.isBracket() || !(leftLeft instanceof SQLUnionQuery) || ((SQLUnionQuery)leftLeft).getOperator() != this.operator) {
                  rights.add(leftRight);
                  rights.add(leftLeft);
                  Collections.reverse(rights);
                  return rights;
               }

               rights.add(leftRight);
               leftUnion = (SQLUnionQuery)leftLeft;
            }
         } else {
            return Arrays.asList(left, right);
         }
      }
   }

   public boolean equals(Object o) {
      if (this == o) {
         return true;
      } else if (o != null && this.getClass() == o.getClass()) {
         SQLUnionQuery that = (SQLUnionQuery)o;
         if (this.bracket != that.bracket) {
            return false;
         } else {
            if (this.relations != null) {
               if (!this.relations.equals(that.relations)) {
                  return false;
               }
            } else if (that.relations != null) {
               return false;
            }

            if (this.operator != that.operator) {
               return false;
            } else {
               if (this.orderBy != null) {
                  if (!this.orderBy.equals(that.orderBy)) {
                     return false;
                  }
               } else if (that.orderBy != null) {
                  return false;
               }

               if (this.limit != null) {
                  if (!this.limit.equals(that.limit)) {
                     return false;
                  }
               } else if (that.limit != null) {
                  return false;
               }

               return this.dbType == that.dbType;
            }
         }
      } else {
         return false;
      }
   }

   public int hashCode() {
      int result = this.bracket ? 1 : 0;
      result = 31 * result + (this.relations != null ? this.relations.hashCode() : 0);
      result = 31 * result + (this.operator != null ? this.operator.hashCode() : 0);
      result = 31 * result + (this.orderBy != null ? this.orderBy.hashCode() : 0);
      result = 31 * result + (this.limit != null ? this.limit.hashCode() : 0);
      result = 31 * result + (this.dbType != null ? this.dbType.hashCode() : 0);
      return result;
   }
}
