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

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLKeep;
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.SQLOver;
import com.alibaba.druid.sql.ast.SQLReplaceable;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.FnvHash;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class SQLAggregateExpr extends SQLMethodInvokeExpr implements Serializable, SQLReplaceable {
   private static final long serialVersionUID = 1L;
   protected SQLAggregateOption option;
   protected SQLKeep keep;
   protected SQLExpr filter;
   protected SQLOver over;
   protected SQLName overRef;
   protected SQLOrderBy orderBy;
   protected boolean withinGroup = false;
   protected Boolean ignoreNulls = false;

   public SQLAggregateExpr(String methodName) {
      this.methodName = methodName;
   }

   public SQLAggregateExpr(String methodName, SQLAggregateOption option) {
      this.methodName = methodName;
      this.option = option;
   }

   public SQLAggregateExpr(String methodName, SQLAggregateOption option, SQLExpr... arguments) {
      this.methodName = methodName;
      this.option = option;
      if (arguments != null) {
         for(SQLExpr argument : arguments) {
            if (argument != null) {
               this.addArgument(argument);
            }
         }
      }

   }

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

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

      this.orderBy = orderBy;
   }

   public SQLAggregateOption getOption() {
      return this.option;
   }

   public void setOption(SQLAggregateOption option) {
      this.option = option;
   }

   public boolean isDistinct() {
      return this.option == SQLAggregateOption.DISTINCT;
   }

   public SQLOver getOver() {
      return this.over;
   }

   public void setOver(SQLOver x) {
      if (x != null) {
         x.setParent(this);
      }

      this.over = x;
   }

   public SQLName getOverRef() {
      return this.overRef;
   }

   public void setOverRef(SQLName x) {
      if (x != null) {
         x.setParent(this);
      }

      this.overRef = x;
   }

   public SQLKeep getKeep() {
      return this.keep;
   }

   public void setKeep(SQLKeep keep) {
      if (keep != null) {
         keep.setParent(this);
      }

      this.keep = keep;
   }

   public boolean isWithinGroup() {
      return this.withinGroup;
   }

   public void setWithinGroup(boolean withinGroup) {
      this.withinGroup = withinGroup;
   }

   /** @deprecated */
   @Deprecated
   public SQLOrderBy getWithinGroup() {
      return this.orderBy;
   }

   public boolean isIgnoreNulls() {
      return this.ignoreNulls != null && this.ignoreNulls;
   }

   public Boolean getIgnoreNulls() {
      return this.ignoreNulls;
   }

   public void setIgnoreNulls(boolean ignoreNulls) {
      this.ignoreNulls = ignoreNulls;
   }

   public String toString() {
      return SQLUtils.toSQLString(this);
   }

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

         for(SQLExpr arg : this.arguments) {
            if (arg != null) {
               arg.accept(visitor);
            }
         }

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

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

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

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

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

      visitor.endVisit(this);
   }

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

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

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

      return children;
   }

   public SQLExpr getFilter() {
      return this.filter;
   }

   public void setFilter(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.filter = x;
   }

   public boolean equals(Object o) {
      if (this == o) {
         return true;
      } else if (o != null && this.getClass() == o.getClass()) {
         if (!super.equals(o)) {
            return false;
         } else {
            SQLAggregateExpr that = (SQLAggregateExpr)o;
            if (this.option != that.option) {
               return false;
            } else {
               if (this.keep != null) {
                  if (!this.keep.equals(that.keep)) {
                     return false;
                  }
               } else if (that.keep != null) {
                  return false;
               }

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

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

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

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

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

   public int hashCode() {
      int result = super.hashCode();
      result = 31 * result + (this.option != null ? this.option.hashCode() : 0);
      result = 31 * result + (this.keep != null ? this.keep.hashCode() : 0);
      result = 31 * result + (this.filter != null ? this.filter.hashCode() : 0);
      result = 31 * result + (this.over != null ? this.over.hashCode() : 0);
      result = 31 * result + (this.overRef != null ? this.overRef.hashCode() : 0);
      result = 31 * result + (this.orderBy != null ? this.orderBy.hashCode() : 0);
      result = 31 * result + (this.ignoreNulls != null ? this.ignoreNulls.hashCode() : 0);
      return result;
   }

   public SQLAggregateExpr clone() {
      SQLAggregateExpr x = new SQLAggregateExpr(this.methodName);
      x.option = this.option;

      for(SQLExpr arg : this.arguments) {
         x.addArgument(arg.clone());
      }

      if (this.keep != null) {
         x.setKeep(this.keep.clone());
      }

      if (this.over != null) {
         x.setOver(this.over.clone());
      }

      if (this.overRef != null) {
         x.setOverRef(this.overRef.clone());
      }

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

      x.ignoreNulls = this.ignoreNulls;
      if (this.attributes != null) {
         for(Map.Entry<String, Object> entry : this.attributes.entrySet()) {
            String key = (String)entry.getKey();
            Object value = entry.getValue();
            if (value instanceof SQLObject) {
               value = ((SQLObject)value).clone();
            }

            x.putAttribute(key, value);
         }
      }

      return x;
   }

   public SQLDataType computeDataType() {
      if (this.resolvedReturnDataType != null) {
         return this.resolvedReturnDataType;
      } else {
         long hash = this.methodNameHashCode64();
         if (hash != FnvHash.Constants.COUNT && hash != FnvHash.Constants.ROW_NUMBER) {
            if (this.arguments.size() > 0) {
               SQLDataType dataType = ((SQLExpr)this.arguments.get(0)).computeDataType();
               if (dataType != null && dataType.nameHashCode64() != FnvHash.Constants.BOOLEAN) {
                  return dataType;
               }
            }

            if (hash == FnvHash.Constants.SUM) {
               return SQLNumberExpr.DATA_TYPE_DOUBLE;
            } else {
               return hash != FnvHash.Constants.WM_CONCAT && hash != FnvHash.Constants.GROUP_CONCAT ? null : SQLCharExpr.DATA_TYPE;
            }
         } else {
            return SQLIntegerExpr.DATA_TYPE;
         }
      }
   }

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

         if (this.overRef == expr) {
            this.setOverRef((SQLName)target);
            return true;
         } else {
            if (this.filter != null) {
               this.filter = target;
               target.setParent(this);
            }

            return false;
         }
      }
   }
}
