package com.chenyang.druid.sql.parser;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.chenyang.druid.sql.dialect.db2.parser.DB2SQLStatementParser;
import com.chenyang.druid.sql.dialect.dm.parser.DmStatementParser;
import com.chenyang.druid.sql.dialect.gauss.parser.GaussSQLStatementParser;
import com.chenyang.druid.sql.dialect.greenplum.parser.GPLexer;
import com.chenyang.druid.sql.dialect.greenplum.parser.GPSQLStatementParser;
import com.chenyang.druid.sql.dialect.hive.parser.HiveStatementParser;
import com.chenyang.druid.sql.dialect.kingbase.parser.KingbaseStatementParser;
import com.chenyang.druid.sql.dialect.mariadb.ast.statement.MariadbSelectQueryBlock;
import com.chenyang.druid.sql.dialect.mariadb.parser.MariadbExprParser;
import com.chenyang.druid.sql.dialect.mariadb.parser.MariadbLexer;
import com.chenyang.druid.sql.dialect.mariadb.parser.MariadbStatementParser;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlExprParser;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlLexer;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleExprParser;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleLexer;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleStatementParser;
import com.chenyang.druid.sql.dialect.phoenix.parser.PhoenixExprParser;
import com.chenyang.druid.sql.dialect.postgresql.parser.PGLexer;
import com.chenyang.druid.sql.dialect.postgresql.parser.PGSQLStatementParser;
import com.chenyang.druid.sql.dialect.presto.parser.PrestoExprParser;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
import com.chenyang.druid.sql.dialect.sqlserver.parser.SQLServerExprParser;
import com.chenyang.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;
import com.chenyang.druid.sql.visitor.SQLASTOutputVisitor;
import com.chenyang.druid.sql.visitor.VisitorFeature;
import com.chenyang.druid.util.StringUtils;

public class SQLParserUtils {
   public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType) {
      SQLParserFeature[] features;
      if (DbType.odps != dbType && DbType.mysql != dbType && DbType.mariadb != dbType) {
         features = new SQLParserFeature[0];
      } else {
         features = new SQLParserFeature[]{SQLParserFeature.KeepComments};
      }

      return createSQLStatementParser(sql, dbType, features);
   }

   public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType, boolean keepComments) {
      SQLParserFeature[] features;
      if (keepComments) {
         features = new SQLParserFeature[]{SQLParserFeature.KeepComments};
      } else {
         features = new SQLParserFeature[0];
      }

      return createSQLStatementParser(sql, dbType, features);
   }

   public static SQLStatementParser createSQLStatementParser(String sql, String dbType, SQLParserFeature... features) {
      return createSQLStatementParser(sql, dbType == null ? null : DbType.valueOf(dbType), features);
   }

   public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType, SQLParserFeature... features) {
      if (dbType == null) {
         dbType = DbType.other;
      }

      switch (dbType) {
         case oracle:
         case oceanbase_oracle:
            return new OracleStatementParser(sql, features);
         case mysql:
         case drds:
            return new MySqlStatementParser(sql, features);
         case mariadb:
            return new MariadbStatementParser(sql, features);
         case elastic_search:
            MySqlStatementParser parser = new MySqlStatementParser(sql, features);
            parser.dbType = dbType;
            parser.exprParser.dbType = dbType;
            return parser;
         case sqlserver:
         case jtds:
            return new SQLServerStatementParser(sql);
         case greenplum:
            return new GPSQLStatementParser(sql, features);
         case dm:
            return new DmStatementParser(sql);
         case db2:
            return new DB2SQLStatementParser(sql, features);
         case kingbase:
            return new KingbaseStatementParser(sql, new SQLParserFeature[]{SQLParserFeature.KeepNameQuotes});
         case postgresql:
            return new PGSQLStatementParser(sql, features);
         case gauss:
            return new GaussSQLStatementParser(sql, features);
         case hive:
            return new HiveStatementParser(sql, features);
         default:
            return new SQLStatementParser(sql, dbType);
      }
   }

   public static SQLExprParser createExprParser(String sql, DbType dbType, SQLParserFeature... features) {
      if (dbType == null) {
         dbType = DbType.other;
      }

      switch (dbType) {
         case oracle:
            return new OracleExprParser(sql, features);
         case oceanbase_oracle:
         case drds:
         case greenplum:
         case dm:
         case db2:
         case kingbase:
         case postgresql:
         case gauss:
         case hive:
         default:
            return new SQLExprParser(sql, dbType, features);
         case mysql:
            return new MySqlExprParser(sql, features);
         case mariadb:
            return new MariadbExprParser(sql, features);
         case elastic_search:
            MySqlExprParser parser = new MySqlExprParser(sql, features);
            parser.dbType = dbType;
            return parser;
         case sqlserver:
         case jtds:
            return new SQLServerExprParser(sql, features);
         case phoenix:
            return new PhoenixExprParser(sql, features);
         case presto:
            return new PrestoExprParser(sql, features);
      }
   }

   public static Lexer createLexer(String sql, DbType dbType) {
       return createLexer(sql, dbType, new SQLParserFeature[0]);
   }

   public static Lexer createLexer(String sql, DbType dbType, SQLParserFeature... features) {
      if (dbType == null) {
         dbType = DbType.other;
      }

      switch (dbType) {
         case oracle:
            return new OracleLexer(sql);
         case oceanbase_oracle:
         case drds:
         case sqlserver:
         case jtds:
         case dm:
         case db2:
         case kingbase:
         default:
            return new Lexer(sql, (Lexer.CommentHandler)null, dbType);
         case mysql:
            return new MySqlLexer(sql);
         case mariadb:
            return new MariadbLexer(sql);
         case elastic_search:
            MySqlLexer lexer = new MySqlLexer(sql);
            lexer.dbType = dbType;
            return lexer;
         case greenplum:
            return new GPLexer(sql, new SQLParserFeature[0]);
         case postgresql:
            return new PGLexer(sql, new SQLParserFeature[0]);
      }
   }

   public static SQLSelectQueryBlock createSelectQueryBlock(DbType dbType) {
      if (dbType == null) {
         dbType = DbType.other;
      }

      switch (dbType) {
         case oracle:
            return new OracleSelectQueryBlock();
         case oceanbase_oracle:
         case drds:
         case elastic_search:
         default:
            return new SQLSelectQueryBlock(dbType);
         case mysql:
            return new MySqlSelectQueryBlock();
         case mariadb:
            return new MariadbSelectQueryBlock();
         case sqlserver:
            return new SQLServerSelectQueryBlock();
      }
   }

   public static SQLType getSQLType(String sql, DbType dbType) {
      Lexer lexer = createLexer(sql, dbType);
      return lexer.scanSQLType();
   }

   public static SQLType getSQLTypeV2(String sql, DbType dbType) {
      Lexer lexer = createLexer(sql, dbType);
      return lexer.scanSQLTypeV2();
   }

   public static boolean startsWithHint(String sql, DbType dbType) {
      Lexer lexer = createLexer(sql, dbType);
      lexer.nextToken();
      return lexer.token() == Token.HINT;
   }

   public static boolean containsAny(String sql, DbType dbType, Token token) {
      Lexer lexer = createLexer(sql, dbType);

      while(true) {
         lexer.nextToken();
         Token tok = lexer.token;
         switch (tok) {
            case EOF:
            case ERROR:
               return false;
            default:
               if (tok == token) {
                  return true;
               }
         }
      }
   }

   public static boolean containsAny(String sql, DbType dbType, Token token1, Token token2) {
      Lexer lexer = createLexer(sql, dbType);

      label18:
      while(true) {
         lexer.nextToken();
         Token tok = lexer.token;
         switch (tok) {
            case EOF:
            case ERROR:
               return false;
            default:
               if (tok == token1 || tok == token2) {
                  break label18;
               }
         }
      }

      return true;
   }

   public static boolean containsAny(String sql, DbType dbType, Token token1, Token token2, Token token3) {
      Lexer lexer = createLexer(sql, dbType);

      label21:
      while(true) {
         lexer.nextToken();
         Token tok = lexer.token;
         switch (tok) {
            case EOF:
            case ERROR:
               return false;
            default:
               if (tok == token1 || tok == token2 || tok == token3) {
                  break label21;
               }
         }
      }

      return true;
   }

   public static boolean containsAny(String sql, DbType dbType, Token... tokens) {
      if (tokens == null) {
         return false;
      } else {
         Lexer lexer = createLexer(sql, dbType);

         while(true) {
            lexer.nextToken();
            Token tok = lexer.token;
            int i;
            switch (tok) {
               case EOF:
               case ERROR:
                  return false;
               default:
                  i = 0;
            }

            while(i < tokens.length) {
               if (tokens[i] == tok) {
                  return true;
               }

               ++i;
            }
         }
      }
   }

   public static Object getSimpleSelectValue(String sql, DbType dbType) {
      return getSimpleSelectValue(sql, dbType, (SimpleValueEvalHandler)null);
   }

   public static Object getSimpleSelectValue(String sql, DbType dbType, SimpleValueEvalHandler handler) {
      Lexer lexer = createLexer(sql, dbType);
      lexer.nextToken();
      if (lexer.token != Token.SELECT && lexer.token != Token.VALUES) {
         return null;
      } else {
         lexer.nextTokenValue();
         SQLExpr expr = null;
         Object value;
         switch (lexer.token) {
            case LITERAL_INT:
               value = lexer.integerValue();
               break;
            case LITERAL_CHARS:
            case LITERAL_NCHARS:
               value = lexer.stringVal();
               break;
            case LITERAL_FLOAT:
               value = lexer.decimalValue();
               break;
            default:
               if (handler == null) {
                  return null;
               }

               expr = (new SQLExprParser(lexer)).expr();

               try {
                  value = handler.eval(expr);
               } catch (Exception var7) {
                  value = null;
               }
         }

         lexer.nextToken();
         if (lexer.token == Token.FROM) {
            lexer.nextToken();
            if (lexer.token != Token.DUAL) {
               return null;
            }

            lexer.nextToken();
         }

         return lexer.token != Token.EOF ? null : value;
      }
   }

   public static String replaceBackQuote(String sql, DbType dbType) {
      int i = sql.indexOf(96);
      if (i == -1) {
         return sql;
      } else {
         char[] chars = sql.toCharArray();
         Lexer lexer = createLexer(sql, dbType);
         int len = chars.length;
         int off = 0;

         while(true) {
            lexer.nextToken();
            switch (lexer.token) {
               case EOF:
               case ERROR:
                  return new String(chars, 0, len);
               case IDENTIFIER:
                  int p0 = lexer.startPos + off;
                  int p1 = lexer.pos - 1 + off;
                  char c0 = chars[p0];
                  char c1 = chars[p1];
                  if (c0 == '`' && c1 == '`') {
                     if (p1 - p0 > 2 && chars[p0 + 1] == '\'' && chars[p1 - 1] == '\'') {
                        System.arraycopy(chars, p0 + 1, chars, p0, p1 - p0 - 1);
                        System.arraycopy(chars, p1 + 1, chars, p1 - 1, chars.length - p1 - 1);
                        len -= 2;
                        off -= 2;
                     } else {
                        chars[p0] = '"';
                        chars[p1] = '"';
                     }
                  }
            }
         }
      }
   }

   public static String addBackQuote(String sql, DbType dbType) {
      if (StringUtils.isEmpty(sql)) {
         return sql;
      } else {
         SQLStatementParser parser = createSQLStatementParser(sql, dbType);
         StringBuffer buf = new StringBuffer(sql.length() + 20);
         SQLASTOutputVisitor out = SQLUtils.createOutputVisitor(buf, DbType.mysql);
         out.config(VisitorFeature.OutputNameQuote, true);
         SQLType sqlType = getSQLType(sql, dbType);
         if (sqlType == SQLType.INSERT) {
            parser.config(SQLParserFeature.InsertReader, true);
            SQLInsertStatement stmt = (SQLInsertStatement)parser.parseStatement();
            int startPos = parser.getLexer().startPos;
            stmt.accept(out);
            if (stmt.getQuery() == null) {
               buf.append(' ');
               buf.append(sql, startPos, sql.length());
            }
         } else {
            SQLStatement stmt = parser.parseStatement();
            stmt.accept(out);
         }

         return buf.toString();
      }
   }

   public interface SimpleValueEvalHandler {
      Object eval(SQLExpr var1);
   }
}
