package util.sqlparse.visitor.sqlserver.visitor;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLCaseExpr;
import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import util.sqlparse.visitor.common.bean.ValueInfo;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.ValueMemo;

public class ValueReplacer {
   private static final String QUESTION_MARK = "?";
   private static final String FUNC_IFNULL = "ifnull";
   private static final String FUNC_NVL = "nvl";
   private Map<String, String> replace;
   private List<Map> whereList;

   public ValueReplacer(Map<String, String> replace) {
      this.replace = replace;
   }

   public ValueReplacer(List<Map> whereList) {
      this.whereList = whereList;
   }

   public void replaceValues(List<ValueInfo> values) {
      if (values != null && values.size() != 0) {
         for(ValueMemo value : values) {
            this.replaceValue(value, null);
         }

      }
   }

   public void replaceValuesWithList(List<ValueInfo> values) {
      if (values != null && values.size() != 0) {
         if (this.whereList != null && this.whereList.size() != 0) {
            for(int i = 0; i < this.whereList.size(); ++i) {
               ValueInfo value = (ValueInfo)values.get(i);
               Map valueMap = (Map)this.whereList.get(i);
               String newVal = (String)valueMap.get("columnValue");
               this.replaceValue(value, newVal);
            }

         }
      }
   }

   private void replaceValue(ValueMemo value, String newVal) {
      if (value != null && value.field != null) {
         SQLExpr valueExpr = (SQLExpr)value.ref;
         if (this.isReplaceField(value, valueExpr)) {
            SQLObject parent = valueExpr.getParent();
            if (parent instanceof SQLBinaryOpExpr) {
               this.replaceBinary(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLCaseExpr.Item) {
               this.replaceCase(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLMethodInvokeExpr) {
               this.replaceMethod(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLInListExpr) {
               this.replaceInList(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLUpdateSetItem) {
               this.replaceSQLUpdateSetItem(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLInsertStatement.ValuesClause) {
               this.replaceSQLInsertClause(value, valueExpr, parent, newVal);
            } else if (parent instanceof SQLBetweenExpr) {
               this.replaceBetween(value, valueExpr, parent, newVal);
            }

         }
      }
   }

   private void replaceBinary(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLBinaryOpExpr opExpr = (SQLBinaryOpExpr)parent;
      SQLExpr rightExpr = opExpr.getRight();
      boolean isRight = rightExpr.equals(valueExpr);
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      if (isRight) {
         opExpr.setRight(valExpr);
      } else {
         opExpr.setLeft(valExpr);
      }

   }

   private void replaceCase(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLCaseExpr.Item itemExpr = (SQLCaseExpr.Item)parent;
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      itemExpr.setValueExpr(valExpr);
   }

   private void replaceMethod(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr)parent;
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      String method = methodExpr.getMethodName();
      if (!method.equalsIgnoreCase("ifnull") && !method.equalsIgnoreCase("nvl")) {
         methodExpr.replace(valueExpr, valExpr);
      } else {
         methodExpr.getArguments().remove(1);
         methodExpr.getArguments().add(valExpr);
      }

   }

   private void replaceInList(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLInListExpr inListExpr = (SQLInListExpr)parent;
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      int index = 0;

      for(int i = 0; i < inListExpr.getTargetList().size(); ++i) {
         if (inListExpr.getTargetList().get(i) == valueExpr) {
            index = i;
            break;
         }
      }

      inListExpr.getTargetList().remove(index);
      inListExpr.getTargetList().add(index, valExpr);
   }

   private void replaceSQLUpdateSetItem(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLUpdateSetItem updateSetItem = (SQLUpdateSetItem)parent;
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      updateSetItem.setValue(valExpr);
   }

   private void replaceSQLInsertClause(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLInsertStatement.ValuesClause valueClause = (SQLInsertStatement.ValuesClause)parent;
      int index = 0;
      List<SQLExpr> values = valueClause.getValues();

      for(int i = 0; i < values.size(); ++i) {
         if (values.get(i) == valueExpr) {
            index = i;
            break;
         }
      }

      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      values.remove(index);
      values.add(index, valExpr);
   }

   private void replaceBetween(ValueMemo value, SQLExpr valueExpr, SQLObject parent, String newVal) {
      SQLBetweenExpr between = (SQLBetweenExpr)parent;
      boolean isBegin = valueExpr == between.getBeginExpr();
      SQLExpr valExpr = this.createValueExpr(value, parent, newVal);
      if (isBegin) {
         between.setBeginExpr(valExpr);
      } else {
         between.setEndExpr(valExpr);
      }

   }

   private boolean isReplaceField(ValueMemo value, SQLExpr valueExpr) {
      if (value.field != null && !valueExpr.toString().equals("?")) {
         if (this.replace == null && this.whereList != null) {
            return true;
         } else if (this.replace != null && value.field.getChildren().size() == 0 && this.replace.containsKey(value.field.getAtomName())) {
            return true;
         } else {
            Set<String> relations = new HashSet();
            this.getAtomFields(value.field, relations);
            return relations.size() == 1;
         }
      } else {
         return false;
      }
   }

   private void getAtomFields(FieldMemo field, Set<String> output) {
      if (field.getChildren().size() == 0 && this.replace != null && this.replace.containsKey(this.replace.get(field.getAtomName()))) {
         output.add(field.getAtomName());
      } else {
         for(FieldMemo child : field.children) {
            this.getAtomFields(child, output);
         }

      }
   }

   private SQLExpr createValueExpr(ValueMemo value, SQLObject parent, String newVal) {
      SQLTextLiteralExpr expr = new MySqlCharExpr();
      expr.setParent(parent);
      if (newVal != null) {
         expr.setText(newVal);
      } else {
         String val = value.value.toString();
         if (this.replace != null && this.replace.containsKey(value.field.getAtomName())) {
            expr.setText((String)this.replace.get(value.field.getAtomName()));
         } else {
            expr.setText(val);
         }
      }

      return expr;
   }
}
