package com.alibaba.druid.sql.visitor;

import com.alibaba.druid.DbType;
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.SQLBooleanExpr;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLDateExpr;
import com.alibaba.druid.sql.ast.expr.SQLHexExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLNullExpr;
import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLTimeExpr;
import com.alibaba.druid.sql.ast.expr.SQLTimestampExpr;
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlExportParameterVisitor;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleExportParameterVisitor;
import com.alibaba.druid.sql.dialect.sqlserver.visitor.MSSQLServerExportParameterVisitor;
import java.util.ArrayList;
import java.util.List;

public final class ExportParameterVisitorUtils {
   private ExportParameterVisitorUtils() {
   }

   public static ExportParameterVisitor createExportParameterVisitor(Appendable out, DbType dbType) {
      if (dbType == null) {
         dbType = DbType.other;
      }

      switch (dbType) {
         case mysql:
         case mariadb:
            return new MySqlExportParameterVisitor(out);
         case oracle:
            return new OracleExportParameterVisitor(out);
         case sqlserver:
         case jtds:
            return new MSSQLServerExportParameterVisitor(out);
         default:
            return new ExportParameterizedOutputVisitor(out);
      }
   }

   public static boolean exportParamterAndAccept(List<Object> parameters, List<SQLExpr> list) {
      int i = 0;

      for(int size = list.size(); i < size; ++i) {
         SQLExpr param = (SQLExpr)list.get(i);
         SQLExpr result = exportParameter(parameters, param);
         if (result != param) {
            list.set(i, result);
         }
      }

      return false;
   }

   public static SQLExpr exportParameter(List<Object> parameters, SQLExpr param) {
      Object value = null;
      boolean replace = false;
      if (param instanceof SQLCharExpr) {
         value = ((SQLCharExpr)param).getText();
         String vStr = (String)value;
         replace = true;
      } else if (param instanceof SQLNCharExpr) {
         value = ((SQLNCharExpr)param).getText();
         replace = true;
      } else if (param instanceof SQLBooleanExpr) {
         value = ((SQLBooleanExpr)param).getBooleanValue();
         replace = true;
      } else if (param instanceof SQLNumericLiteralExpr) {
         value = ((SQLNumericLiteralExpr)param).getNumber();
         replace = true;
      } else if (param instanceof SQLHexExpr) {
         value = ((SQLHexExpr)param).toBytes();
         replace = true;
      } else if (param instanceof SQLTimestampExpr) {
         value = ((SQLTimestampExpr)param).getValue();
         replace = true;
      } else if (param instanceof SQLDateExpr) {
         value = ((SQLDateExpr)param).getValue();
         replace = true;
      } else if (param instanceof SQLTimeExpr) {
         value = ((SQLTimeExpr)param).getValue();
         replace = true;
      } else if (param instanceof SQLListExpr) {
         SQLListExpr list = (SQLListExpr)param;
         List<Object> listValues = new ArrayList();

         for(int i = 0; i < list.getItems().size(); ++i) {
            SQLExpr listItem = (SQLExpr)list.getItems().get(i);
            if (listItem instanceof SQLCharExpr) {
               Object listValue = ((SQLCharExpr)listItem).getText();
               listValues.add(listValue);
            } else if (listItem instanceof SQLBooleanExpr) {
               Object listValue = ((SQLBooleanExpr)listItem).getBooleanValue();
               listValues.add(listValue);
            } else if (listItem instanceof SQLNumericLiteralExpr) {
               Object listValue = ((SQLNumericLiteralExpr)listItem).getNumber();
               listValues.add(listValue);
            } else if (param instanceof SQLHexExpr) {
               Object listValue = ((SQLHexExpr)listItem).toBytes();
               listValues.add(listValue);
            }
         }

         if (listValues.size() == list.getItems().size()) {
            value = listValues;
            replace = true;
         }
      } else if (param instanceof SQLNullExpr) {
         value = null;
         replace = true;
      }

      if (!replace) {
         return param;
      } else {
         SQLObject parent = param.getParent();
         if (parent != null) {
            List<SQLObject> mergedList = null;
            if (parent instanceof SQLBinaryOpExpr) {
               mergedList = ((SQLBinaryOpExpr)parent).getMergedList();
            }

            if (mergedList != null) {
               List<Object> mergedListParams = new ArrayList(mergedList.size() + 1);

               for(int i = 0; i < mergedList.size(); ++i) {
                  SQLObject item = (SQLObject)mergedList.get(i);
                  if (item instanceof SQLBinaryOpExpr) {
                     SQLBinaryOpExpr binaryOpItem = (SQLBinaryOpExpr)item;
                     exportParameter(mergedListParams, binaryOpItem.getRight());
                  }
               }

               if (mergedListParams.size() > 0) {
                  mergedListParams.add(0, value);
                  value = mergedListParams;
               }
            }
         }

         if (parameters != null) {
            parameters.add(value);
         }

         return new SQLVariantRefExpr("?");
      }
   }

   public static void exportParameter(List<Object> parameters, SQLBinaryOpExpr x) {
      if (!(x.getLeft() instanceof SQLLiteralExpr) || !(x.getRight() instanceof SQLLiteralExpr) || !x.getOperator().isRelational()) {
         SQLExpr leftResult = exportParameter(parameters, x.getLeft());
         if (leftResult != x.getLeft()) {
            x.setLeft(leftResult);
         }

         leftResult = exportParameter(parameters, x.getRight());
         if (leftResult != x.getRight()) {
            x.setRight(leftResult);
         }

      }
   }

   public static void exportParameter(List<Object> parameters, SQLBetweenExpr x) {
      SQLExpr result = exportParameter(parameters, x.getBeginExpr());
      if (result != x.getBeginExpr()) {
         x.setBeginExpr(result);
      }

      result = exportParameter(parameters, x.getEndExpr());
      if (result != x.getBeginExpr()) {
         x.setEndExpr(result);
      }

   }
}
