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

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLReplaceable;
import com.chenyang.druid.sql.ast.SQLStatementImpl;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExprGroup;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import java.util.ArrayList;
import java.util.List;

public class SQLDeleteStatement extends SQLStatementImpl implements SQLReplaceable {
   protected SQLWithSubqueryClause with;
   protected SQLTableSource tableSource;
   protected SQLExpr where;
   protected SQLTableSource from;
   protected SQLTableSource using;
   protected boolean only = false;

   public SQLDeleteStatement() {
   }

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

   protected void cloneTo(SQLDeleteStatement x) {
      if (this.headHints != null) {
         for(SQLCommentHint h : this.headHints) {
            SQLCommentHint h2 = h.clone();
            h2.setParent(x);
            x.headHints.add(h2);
         }
      }

      if (this.with != null) {
         x.setWith(this.with.clone());
      }

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

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

      if (this.from != null) {
         x.setFrom(this.from.clone());
      }

      if (this.using != null) {
         x.setUsing(this.using.clone());
      }

      x.only = this.only;
   }

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

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

   public SQLExprTableSource getExprTableSource() {
      return (SQLExprTableSource)this.getTableSource();
   }

   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) {
         SQLExprTableSource exprTableSource = (SQLExprTableSource)this.tableSource;
         return (SQLName)exprTableSource.getExpr();
      } else {
         if (this.tableSource instanceof SQLSubqueryTableSource) {
            SQLSelectQuery selectQuery = ((SQLSubqueryTableSource)this.tableSource).getSelect().getQuery();
            if (selectQuery instanceof SQLSelectQueryBlock) {
               SQLTableSource subQueryTableSource = ((SQLSelectQueryBlock)selectQuery).getFrom();
               if (subQueryTableSource instanceof SQLExprTableSource) {
                  SQLExpr subQueryTableSourceExpr = ((SQLExprTableSource)subQueryTableSource).getExpr();
                  return (SQLName)subQueryTableSourceExpr;
               }
            }
         }

         return null;
      }
   }

   public void setTableName(SQLName tableName) {
      this.setTableSource((SQLTableSource)(new SQLExprTableSource(tableName)));
   }

   public void setTableName(String name) {
      this.setTableName((SQLName)(new SQLIdentifierExpr(name)));
   }

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

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

      this.where = where;
   }

   public String getAlias() {
      return this.tableSource.getAlias();
   }

   public void setAlias(String alias) {
      this.tableSource.setAlias(alias);
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         if (this.with != null) {
            this.with.accept(visitor);
         }

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

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

      visitor.endVisit(this);
   }

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

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

      return children;
   }

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

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

      this.from = from;
   }

   public boolean replace(SQLExpr expr, SQLExpr target) {
      if (this.where == expr) {
         this.setWhere(target);
         return true;
      } else {
         return false;
      }
   }

   public boolean isOnly() {
      return this.only;
   }

   public void setOnly(boolean only) {
      this.only = only;
   }

   public SQLTableSource getUsing() {
      return this.using;
   }

   public void setUsing(SQLTableSource using) {
      this.using = using;
   }

   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 addWhere(SQLExpr where) {
      if (where == null) {
         return false;
      } else {
         this.addCondition(where);
         return true;
      }
   }
}
