package util.sqlparse.visitor.postgresql.visitor;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.TableMemo;
import util.sqlparse.visitor.common.memo.ValueMemo;
import util.sqlparse.visitor.common.scope.ReplaceInfo;
import util.sqlparse.visitor.common.scope.Scope;

public class ReplaceVisitor extends ParseVisitor {
   public static String LIKE_FUNCTION = "jz_ext_like";
   public static String SINGLE_QUOTATION_MARK = "'";

   public ReplaceVisitor(Scope scope) {
      super(scope);
   }

   public boolean visit(SQLBinaryOpExpr x) {
      if (x.getOperator().equals(SQLBinaryOperator.Like)) {
         SQLExpr left = x.getLeft();
         ValueMemo value = this.getValueMemo(left, (SQLExpr)null);
         value.expr = x;
         value.operator = x.getOperator().getName().toUpperCase();
         ReplaceInfo replaceInfo = new ReplaceInfo();
         replaceInfo.expr = x;
         String columnName = "";
         if (x.getLeft() instanceof SQLIdentifierExpr) {
            columnName = ((SQLIdentifierExpr)x.getLeft()).getName();
         } else if (x.getLeft() instanceof SQLPropertyExpr) {
            columnName = ((SQLPropertyExpr)x.getLeft()).getName();
         }

         replaceInfo.columnName = columnName;
         FieldMemo field = value.field;
         TableMemo table = field.table;
         replaceInfo.prefix = table.schema + "_" + table.name;
         this.scope.setReplaceInfo(replaceInfo);
      }

      return super.visit((SQLBinaryOpExpr)x);
   }

   public String visitReplace(SQLStatement stmt, ReplaceInfo replaceInfo, String encType, String secretKey) {
      SQLBinaryOpExpr x = (SQLBinaryOpExpr)replaceInfo.expr;
      SQLMethodInvokeExpr method = new SQLMethodInvokeExpr();
      method.setMethodName(LIKE_FUNCTION);
      method.addArgument(new SQLCharExpr(encType));
      method.addArgument(x.getLeft());
      method.addArgument(x.getRight());
      method.addArgument(new SQLCharExpr(secretKey));
      if (x.getParent() instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock parent = (SQLSelectQueryBlock)x.getParent();
         parent.setWhere(method);
         method.setParent(parent);
      } else if (x.getParent() instanceof SQLBinaryOpExpr) {
         SQLBinaryOpExpr parent = (SQLBinaryOpExpr)x.getParent();
         SQLExpr left = parent.getLeft();
         if (left == x) {
            method.setParent(parent);
            parent.setLeft(method);
         } else {
            method.setParent(parent);
            parent.setRight(method);
         }
      }

      return stmt.toString();
   }

   private ValueMemo getValueMemo(SQLExpr x, SQLExpr valueExpr) {
      return this.getValueMemo(x, valueExpr, false);
   }

   private ValueMemo getValueMemo(SQLExpr x, SQLExpr valueExpr, boolean nullIfNoField) {
      ValueMemo value = this.scope.getCurrent().getFieldValueInfo(x);
      if (value == null && !nullIfNoField) {
         value = new ValueMemo(valueExpr);
         FieldMemo field = new FieldMemo();
         value.field = field;
      }

      return value;
   }
}
