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

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLIndexDefinition;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import java.util.List;

public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint, SQLTableElement {
   protected final SQLIndexDefinition indexDefinition = new SQLIndexDefinition();

   public SQLUnique() {
      this.indexDefinition.setParent(this);
   }

   public SQLName getName() {
      return this.indexDefinition.getName();
   }

   public void setName(SQLName name) {
      this.indexDefinition.setName(name);
   }

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

   public SQLExpr getComment() {
      return this.indexDefinition.hasOptions() ? this.indexDefinition.getOptions().getComment() : null;
   }

   public void setComment(SQLExpr x) {
      this.indexDefinition.getOptions().setComment(x);
   }

   public SQLIndexDefinition getIndexDefinition() {
      return this.indexDefinition;
   }

   public List<SQLSelectOrderByItem> getColumns() {
      return this.indexDefinition.getColumns();
   }

   public void addColumn(SQLExpr column) {
      if (column != null) {
         this.addColumn(new SQLSelectOrderByItem(column));
      }
   }

   public void addColumn(SQLSelectOrderByItem column) {
      if (column != null) {
         column.setParent(this);
      }

      this.indexDefinition.getColumns().add(column);
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         this.acceptChild(visitor, this.getName());
         this.acceptChild(visitor, this.getColumns());
         this.acceptChild(visitor, this.getCovering());
      }

      visitor.endVisit(this);
   }

   public boolean containsColumn(String column) {
      for(SQLSelectOrderByItem item : this.getColumns()) {
         SQLExpr expr = item.getExpr();
         if (expr instanceof SQLIdentifierExpr && SQLUtils.nameEquals(((SQLIdentifierExpr)expr).getName(), column)) {
            return true;
         }
      }

      return false;
   }

   public boolean containsColumn(long columnNameHash) {
      for(SQLSelectOrderByItem item : this.getColumns()) {
         SQLExpr expr = item.getExpr();
         if (expr instanceof SQLIdentifierExpr && ((SQLIdentifierExpr)expr).nameHashCode64() == columnNameHash) {
            return true;
         }
      }

      return false;
   }

   public void cloneTo(SQLUnique x) {
      super.cloneTo((SQLConstraintImpl)x);
      this.indexDefinition.cloneTo(x.indexDefinition);
   }

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

   public void simplify() {
      super.simplify();

      for(SQLSelectOrderByItem item : this.getColumns()) {
         SQLExpr column = item.getExpr();
         if (column instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identExpr = (SQLIdentifierExpr)column;
            String columnName = identExpr.getName();
            String normalized = SQLUtils.normalize(columnName, this.dbType);
            if (normalized != columnName) {
               item.setExpr(new SQLIdentifierExpr(columnName));
            }
         }
      }

   }

   public boolean applyColumnRename(SQLName columnName, SQLColumnDefinition to) {
      for(SQLSelectOrderByItem orderByItem : this.getColumns()) {
         SQLExpr expr = orderByItem.getExpr();
         if (expr instanceof SQLName && SQLUtils.nameEquals((SQLName)expr, columnName)) {
            orderByItem.setExpr(to.getName().clone());
            return true;
         }

         if (expr instanceof SQLMethodInvokeExpr && SQLUtils.nameEquals(((SQLMethodInvokeExpr)expr).getMethodName(), columnName.getSimpleName()) && 1 == ((SQLMethodInvokeExpr)expr).getArguments().size() && ((SQLMethodInvokeExpr)expr).getArguments().get(0) instanceof SQLIntegerExpr) {
            if (to.getDataType().hasKeyLength() && 1 == to.getDataType().getArguments().size() && to.getDataType().getArguments().get(0) instanceof SQLIntegerExpr) {
               int newKeyLength = ((SQLIntegerExpr)to.getDataType().getArguments().get(0)).getNumber().intValue();
               int oldKeyLength = ((SQLIntegerExpr)((SQLMethodInvokeExpr)expr).getArguments().get(0)).getNumber().intValue();
               if (newKeyLength > oldKeyLength) {
                  ((SQLMethodInvokeExpr)expr).setMethodName(to.getName().getSimpleName());
                  return true;
               }
            }

            orderByItem.setExpr(to.getName().clone());
            return true;
         }
      }

      return false;
   }

   public boolean applyDropColumn(SQLName columnName) {
      for(int i = this.getColumns().size() - 1; i >= 0; --i) {
         SQLExpr expr = ((SQLSelectOrderByItem)this.getColumns().get(i)).getExpr();
         if (expr instanceof SQLName && SQLUtils.nameEquals((SQLName)expr, columnName)) {
            this.getColumns().remove(i);
            return true;
         }

         if (expr instanceof SQLMethodInvokeExpr && SQLUtils.nameEquals(((SQLMethodInvokeExpr)expr).getMethodName(), columnName.getSimpleName())) {
            this.getColumns().remove(i);
            return true;
         }
      }

      return false;
   }

   public List<SQLName> getCovering() {
      return this.indexDefinition.getCovering();
   }
}
