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

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLReplaceable;
import com.alibaba.druid.sql.ast.SQLStatementImpl;
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.visitor.SQLASTVisitor;
import java.util.ArrayList;
import java.util.List;

public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceable {
   protected SQLWithSubqueryClause with;
   protected final List<SQLUpdateSetItem> items = new ArrayList();
   protected SQLExpr where;
   protected SQLTableSource from;
   protected SQLTableSource tableSource;
   public List<SQLExpr> returning;
   protected SQLOrderBy orderBy;

   public SQLUpdateStatement() {
   }

   public void cloneTo(SQLUpdateStatement x) {
      x.dbType = this.dbType;
      x.afterSemi = this.afterSemi;
      if (this.with != null) {
         x.setWith(this.with.clone());
         x.with.setParent(x);
      }

      if (this.where != null) {
         x.where = this.where.clone();
         x.where.setParent(x);
      }

      if (this.tableSource != null) {
         x.setTableSource(this.tableSource.clone());
         x.tableSource.setParent(x);
      }

      for(SQLUpdateSetItem item : this.items) {
         SQLUpdateSetItem clone = item.clone();
         clone.setParent(x);
         x.getItems().add(clone);
      }

      if (this.returning != null) {
         for(SQLExpr item : this.returning) {
            SQLExpr clone = item.clone();
            clone.setParent(x);
            x.getReturning().add(clone);
         }
      }

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

   }

   public SQLUpdateStatement clone() {
      SQLUpdateStatement x = new SQLUpdateStatement();
      this.cloneTo(x);
      return x;
   }

   public SQLUpdateStatement(DbType dbType) {
      super(dbType);
   }

   public SQLTableSource getTableSource() {
      return this.tableSource;
   }

   public void setTableSource(SQLExpr expr) {
      this.setTableSource((SQLTableSource)(new SQLExprTableSource(expr)));
   }

   public void setTableSource(SQLTableSource tableSource) {
      if (tableSource != null) {
         tableSource.setParent(this);
      }

      this.tableSource = tableSource;
   }

   public SQLName getTableName() {
      if (this.tableSource instanceof SQLExprTableSource) {
         return ((SQLExprTableSource)this.tableSource).getName();
      } else {
         if (this.tableSource instanceof SQLJoinTableSource) {
            SQLTableSource left = ((SQLJoinTableSource)this.tableSource).getLeft();
            if (left instanceof SQLExprTableSource) {
               return ((SQLExprTableSource)left).getName();
            }
         }

         return null;
      }
   }

   public SQLExpr getWhere() {
      return this.where;
   }

   public void setWhere(SQLExpr where) {
      if (where != null) {
         where.setParent(this);
      }

      this.where = where;
   }

   public List<SQLUpdateSetItem> getItems() {
      return this.items;
   }

   public void addItem(SQLUpdateSetItem item) {
      this.items.add(item);
      item.setParent(this);
   }

   public List<SQLExpr> getReturning() {
      if (this.returning == null) {
         this.returning = new ArrayList(2);
      }

      return this.returning;
   }

   public SQLTableSource getFrom() {
      return this.from;
   }

   public void setFrom(SQLTableSource from) {
      if (from != null) {
         from.setParent(this);
      }

      this.from = from;
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         this.acceptChild(visitor);
      }

      visitor.endVisit(this);
   }

   protected void acceptChild(SQLASTVisitor visitor) {
      if (this.tableSource != null) {
         this.tableSource.accept(visitor);
      }

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

      for(int i = 0; i < this.items.size(); ++i) {
         SQLUpdateSetItem item = (SQLUpdateSetItem)this.items.get(i);
         if (item != null) {
            item.accept(visitor);
         }
      }

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

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

   }

   public List<SQLObject> getChildren() {
      List<SQLObject> children = new ArrayList();
      if (this.tableSource != null) {
         children.add(this.tableSource);
      }

      if (this.from != null) {
         children.add(this.from);
      }

      children.addAll(this.items);
      if (this.where != null) {
         children.add(this.where);
      }

      if (this.orderBy != null) {
         children.add(this.orderBy);
      }

      return children;
   }

   public boolean replace(SQLExpr expr, SQLExpr target) {
      if (this.where == expr) {
         this.setWhere(target);
         return true;
      } else {
         if (this.returning != null) {
            for(int i = 0; i < this.returning.size(); ++i) {
               if (this.returning.get(i) == expr) {
                  target.setParent(this);
                  this.returning.set(i, target);
                  return true;
               }
            }
         }

         return false;
      }
   }

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

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

      this.orderBy = orderBy;
   }

   public SQLWithSubqueryClause getWith() {
      return this.with;
   }

   public void setWith(SQLWithSubqueryClause with) {
      if (with != null) {
         with.setParent(this);
      }

      this.with = with;
   }

   public void addCondition(String conditionSql) {
      if (conditionSql != null && conditionSql.length() != 0) {
         SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, this.dbType);
         this.addCondition(condition);
      }
   }

   public void addCondition(SQLExpr expr) {
      if (expr != null) {
         this.setWhere(SQLBinaryOpExpr.and(this.where, expr));
      }
   }

   public boolean removeCondition(String conditionSql) {
      if (conditionSql != null && conditionSql.length() != 0) {
         SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, this.dbType);
         return this.removeCondition(condition);
      } else {
         return false;
      }
   }

   public boolean removeCondition(SQLExpr condition) {
      if (condition == null) {
         return false;
      } else if (this.where instanceof SQLBinaryOpExprGroup) {
         SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup)this.where;
         int removedCount = 0;
         List<SQLExpr> items = group.getItems();

         for(int i = items.size() - 1; i >= 0; --i) {
            if (((SQLExpr)items.get(i)).equals(condition)) {
               items.remove(i);
               ++removedCount;
            }
         }

         if (items.size() == 0) {
            this.where = null;
         }

         return removedCount > 0;
      } else {
         if (this.where instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr binaryOpWhere = (SQLBinaryOpExpr)this.where;
            SQLBinaryOperator operator = binaryOpWhere.getOperator();
            if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) {
               List<SQLExpr> items = SQLBinaryOpExpr.split(binaryOpWhere);
               int removedCount = 0;

               for(int i = items.size() - 1; i >= 0; --i) {
                  SQLExpr item = (SQLExpr)items.get(i);
                  if (item.equals(condition) && SQLUtils.replaceInParent((SQLExpr)item, (SQLExpr)null)) {
                     ++removedCount;
                  }
               }

               return removedCount > 0;
            }
         }

         if (condition.equals(this.where)) {
            this.where = null;
            return true;
         } else {
            return false;
         }
      }
   }

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

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

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

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

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

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

   public int hashCode() {
      int result = this.with != null ? this.with.hashCode() : 0;
      result = 31 * result + this.items.hashCode();
      result = 31 * result + (this.where != null ? this.where.hashCode() : 0);
      result = 31 * result + (this.from != null ? this.from.hashCode() : 0);
      result = 31 * result + (this.tableSource != null ? this.tableSource.hashCode() : 0);
      result = 31 * result + (this.returning != null ? this.returning.hashCode() : 0);
      result = 31 * result + (this.orderBy != null ? this.orderBy.hashCode() : 0);
      return result;
   }

   public boolean addWhere(SQLExpr where) {
      if (where == null) {
         return false;
      } else {
         this.addCondition(where);
         return true;
      }
   }
}
