package com.chenyang.druid.sql.dialect.kingbase.parser.lexer;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.parser.CharTypes;
import com.chenyang.druid.sql.parser.Keywords;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.NotAllowCommentException;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLParserFeature;
import com.chenyang.druid.sql.parser.Token;
import java.util.HashMap;
import java.util.Map;

public class KingbaseLexer extends Lexer {
   public boolean tablesource;
   public static final Keywords DEFAULT_KINGBASE_KEYWORDS;

   public KingbaseLexer(String input, CommentHandler commentHandler) {
      super(input, commentHandler);
   }

   public KingbaseLexer(String input, CommentHandler commentHandler, DbType dbType) {
      super(input, commentHandler, dbType);
      super.keywords = DEFAULT_KINGBASE_KEYWORDS;
   }

   public KingbaseLexer(String input, boolean skipComment, SQLParserFeature... features) {
      super(input, skipComment);
      super.keywords = DEFAULT_KINGBASE_KEYWORDS;
      this.nextToken();

      for(SQLParserFeature feature : features) {
         this.config(feature, true);
      }

   }

   public KingbaseLexer() {
      super("input");
   }

   public KingbaseLexer(char[] input, int inputLength, boolean skipComment) {
      super(input, inputLength, skipComment);
      super.keywords = DEFAULT_KINGBASE_KEYWORDS;
   }

   public KingbaseLexer(String input) {
      super(input);
      this.skipComment = true;
      this.keepComments = true;
      super.keywords = DEFAULT_KINGBASE_KEYWORDS;
   }

   public KingbaseLexer(String input, SQLParserFeature... features) {
      super(input);
      this.skipComment = true;
      this.keepComments = true;
      super.keywords = DEFAULT_KINGBASE_KEYWORDS;

      for(SQLParserFeature feature : features) {
         this.config(feature, true);
      }

   }

   public void scanVariable() {
      char c0 = this.ch;
      if (c0 != ':' && c0 != '#' && c0 != '$') {
         throw new ParserException("illegal variable. " + this.info());
      } else {
         this.mark = this.pos;
         this.bufPos = 1;
         boolean quoteFlag = false;
         boolean mybatisFlag = false;
         char c1 = this.charAt(this.pos + 1);
         if (c0 == ':' && c1 == ' ') {
            ++this.pos;
            this.bufPos = 2;
            c1 = this.charAt(this.pos + 1);
         }

         if (c1 == '"') {
            ++this.pos;
            ++this.bufPos;
            quoteFlag = true;
         } else if (c1 == '{') {
            ++this.pos;
            ++this.bufPos;
            mybatisFlag = true;
         }

         char ch;
         if (c0 == ':' && c1 >= '0' && c1 <= '9') {
            while(true) {
               ch = this.charAt(++this.pos);
               if (ch < '0' || ch > '9') {
                  break;
               }

               ++this.bufPos;
            }
         } else {
            while(true) {
               ch = this.charAt(++this.pos);
               if (!CharTypes.isIdentifierChar(ch) && ch != ':') {
                  break;
               }

               ++this.bufPos;
            }
         }

         if (quoteFlag) {
            if (ch != '"') {
               throw new ParserException("syntax error. " + this.info());
            }

            ++this.pos;
            ++this.bufPos;
         } else if (mybatisFlag) {
            if (ch != '}') {
               throw new ParserException("syntax error" + this.info());
            }

            ++this.pos;
            ++this.bufPos;
         }

         this.ch = this.charAt(this.pos);
         this.stringVal = this.addSymbol();
         Token tok = this.keywords.getKeyword(this.stringVal);
         if (tok != null) {
            this.token = tok;
         } else {
            this.token = Token.VARIANT;
         }

      }
   }

   protected void scanVariable_at() {
      this.scanChar();
      if (this.ch == '@') {
         this.scanChar();
         this.token = Token.MONKEYS_AT_AT;
      } else {
         this.token = Token.MONKEYS_AT;
      }

   }

   public void scanComment() {
      if (this.ch != '/' && this.ch != '-') {
         throw new IllegalStateException();
      } else {
         this.mark = this.pos;
         this.bufPos = 0;
         this.scanChar();
         if (this.ch == '*') {
            this.scanChar();
            ++this.bufPos;

            while(this.ch == ' ') {
               this.scanChar();
               ++this.bufPos;
            }

            boolean isHint = false;
            int startHintSp = this.bufPos + 1;
            if (this.ch == '+') {
               isHint = true;
               this.scanChar();
               ++this.bufPos;
            }

            while(!this.isEOF()) {
               if (this.ch == '*' && this.charAt(this.pos + 1) == '/') {
                  this.bufPos += 2;
                  this.scanChar();
                  this.scanChar();
                  break;
               }

               this.scanChar();
               ++this.bufPos;
            }

            if (isHint) {
               this.stringVal = this.subString(this.mark + startHintSp, this.bufPos - startHintSp - 1);
               this.token = Token.HINT;
            } else {
               this.stringVal = this.subString(this.mark, this.bufPos + 1);
               this.token = Token.MULTI_LINE_COMMENT;
               ++this.commentCount;
               if (this.keepComments) {
                  this.addComment(this.stringVal);
               }
            }

            if (this.token != Token.HINT && !this.isAllowComment()) {
               throw new NotAllowCommentException();
            }
         } else if (!this.isAllowComment()) {
            throw new NotAllowCommentException();
         } else if (this.ch == '/' || this.ch == '-') {
            this.scanChar();
            ++this.bufPos;

            while(true) {
               if (this.ch == '\r') {
                  if (this.charAt(this.pos + 1) == '\n') {
                     this.bufPos += 2;
                     this.scanChar();
                  } else {
                     ++this.bufPos;
                  }
                  break;
               }

               if (this.ch == 26) {
                  break;
               }

               if (this.ch == '\n') {
                  this.scanChar();
                  ++this.bufPos;
                  break;
               }

               this.scanChar();
               ++this.bufPos;
            }

            this.stringVal = this.subString(this.mark, this.ch != 26 ? this.bufPos : this.bufPos + 1);
            this.token = Token.LINE_COMMENT;
            ++this.commentCount;
            if (this.keepComments) {
               this.addComment(this.stringVal);
            }

            this.endOfComment = this.isEOF();
         }
      }
   }

   public void scanNumber() {
      this.mark = this.pos;
      if (this.ch == '-') {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      while(this.ch >= '0' && this.ch <= '9') {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      boolean isDouble = false;
      if (this.ch == '.') {
         if (this.charAt(this.pos + 1) == '.') {
            this.token = Token.LITERAL_INT;
            return;
         }

         ++this.bufPos;
         this.ch = this.charAt(++this.pos);

         for(isDouble = true; this.ch >= '0' && this.ch <= '9'; this.ch = this.charAt(++this.pos)) {
            ++this.bufPos;
         }
      }

      if ((this.ch == 'e' || this.ch == 'E') && isDigit2(this.charAt(this.pos + 1))) {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
         if (this.ch == '+' || this.ch == '-') {
            ++this.bufPos;
            this.ch = this.charAt(++this.pos);
         }

         while(this.ch >= '0' && this.ch <= '9') {
            ++this.bufPos;
            this.ch = this.charAt(++this.pos);
         }

         isDouble = true;
      }

      if (this.ch != 'f' && this.ch != 'F') {
         if (this.ch != 'd' && this.ch != 'D') {
            if (isDouble) {
               this.token = Token.LITERAL_FLOAT;
            } else {
               this.token = Token.LITERAL_INT;
            }

         } else {
            this.token = Token.BINARY_DOUBLE;
            this.scanChar();
         }
      } else {
         this.token = Token.BINARY_FLOAT;
         this.scanChar();
      }
   }

   public void nextTokenValue() {
      this.startPos = this.pos;

      while(this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == 'B' || this.ch == 'b') {
         this.scanChar();
         if (this.ch == '\'') {
            this.bufPos = 0;
            this.scanString();
            this.token = Token.LITERAL_BCHARS;
            return;
         }

         this.unscan();
      }

      super.nextTokenValue();
   }

   static {
      Map<String, Token> map = new HashMap();
      map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
      map.put("BEGIN", Token.BEGIN);
      map.put("COMMENT", Token.COMMENT);
      map.put("COMMIT", Token.COMMIT);
      map.put("CONNECT", Token.CONNECT);
      map.put("CONTINUE", Token.CONTINUE);
      map.put("CROSS", Token.CROSS);
      map.put("CURSOR", Token.CURSOR);
      map.put("DECLARE", Token.DECLARE);
      map.put("ERRORS", Token.ERRORS);
      map.put("EXCEPTION", Token.EXCEPTION);
      map.put("EXCLUSIVE", Token.EXCLUSIVE);
      map.put("EXTRACT", Token.EXTRACT);
      map.put("GOTO", Token.GOTO);
      map.put("IF", Token.IF);
      map.put("ELSIF", Token.ELSIF);
      map.put("LIMIT", Token.LIMIT);
      map.put("OFFSET", Token.OFFSET);
      map.put("LOOP", Token.LOOP);
      map.put("MATCHED", Token.MATCHED);
      map.put("MERGE", Token.MERGE);
      map.put("MODE", Token.MODE);
      map.put("NOWAIT", Token.NOWAIT);
      map.put("OF", Token.OF);
      map.put("PRIOR", Token.PRIOR);
      map.put("REJECT", Token.REJECT);
      map.put("RETURN", Token.RETURN);
      map.put("RETURNING", Token.RETURNING);
      map.put("SAVEPOINT", Token.SAVEPOINT);
      map.put("SESSION", Token.SESSION);
      map.put("SHARE", Token.SHARE);
      map.put("START", Token.START);
      map.put("SYSDATE", Token.SYSDATE);
      map.put("UNLIMITED", Token.UNLIMITED);
      map.put("USING", Token.USING);
      map.put("WAIT", Token.WAIT);
      map.put("WITH", Token.WITH);
      map.put("WITHOUT", Token.WITHOUT);
      map.put("LOCAL", Token.LOCAL);
      map.put("PCTFREE", Token.PCTFREE);
      map.put("INITRANS", Token.INITRANS);
      map.put("MAXTRANS", Token.MAXTRANS);
      map.put("SEGMENT", Token.SEGMENT);
      map.put("CREATION", Token.CREATION);
      map.put("IMMEDIATE", Token.IMMEDIATE);
      map.put("DEFERRED", Token.DEFERRED);
      map.put("STORAGE", Token.STORAGE);
      map.put("NEXT", Token.NEXT);
      map.put("MINEXTENTS", Token.MINEXTENTS);
      map.put("MAXEXTENTS", Token.MAXEXTENTS);
      map.put("MAXSIZE", Token.MAXSIZE);
      map.put("PCTINCREASE", Token.PCTINCREASE);
      map.put("FLASH_CACHE", Token.FLASH_CACHE);
      map.put("CELL_FLASH_CACHE", Token.CELL_FLASH_CACHE);
      map.put("NONE", Token.NONE);
      map.put("LOB", Token.LOB);
      map.put("STORE", Token.STORE);
      map.put("ROW", Token.ROW);
      map.put("CHUNK", Token.CHUNK);
      map.put("CACHE", Token.CACHE);
      map.put("NOCACHE", Token.NOCACHE);
      map.put("LOGGING", Token.LOGGING);
      map.put("NOCOMPRESS", Token.NOCOMPRESS);
      map.put("KEEP_DUPLICATES", Token.KEEP_DUPLICATES);
      map.put("EXCEPTIONS", Token.EXCEPTIONS);
      map.put("PURGE", Token.PURGE);
      map.put("INITIALLY", Token.INITIALLY);
      map.put("FETCH", Token.FETCH);
      map.put("ALL", Token.ALL);
      map.put("TABLESPACE", Token.TABLESPACE);
      map.put("PARTITION", Token.PARTITION);
      map.put("TRUE", Token.TRUE);
      map.put("FALSE", Token.FALSE);
      map.put("EXCLUDE", Token.EXCLUDE);
      map.put("TIMESTAMP", Token.TIMESTAMP);
      map.put("TIMESTAMPTZ", Token.TIMESTAMPTZ);
      map.put("TIME", Token.TIME);
      map.put("DATE", Token.DATE);
      map.put("INTERVAL", Token.INTERVAL);
      map.put("YEAR", Token.YEAR);
      map.put("MONTH", Token.MONTH);
      map.put("DAY", Token.DAY);
      map.put("HOUR", Token.HOUR);
      map.put("MINUTE", Token.MINUTE);
      map.put("SECOND", Token.SECOND);
      map.put("YEAR_MONTH", Token.YEAR_MONTH);
      map.put("DAY_MINUTE", Token.DAY_MINUTE);
      map.put("DAY_SECOND", Token.DAY_SECOND);
      map.put("HOUR_MINUTE", Token.HOUR_MINUTE);
      map.put("HOUR_SECOND", Token.HOUR_SECOND);
      map.put("MINUTE_SECOND", Token.MINUTE_SECOND);
      map.put("，", Token.COMMA);
      map.put("（", Token.LPAREN);
      map.put("）", Token.RPAREN);
      map.put("ISNULL", Token.ISNULL);
      map.put("COLONCOLON", Token.COLONCOLON);
      map.put("NOTNULL", Token.NOTNULL);
      map.put("ONLY", Token.ONLY);
      map.put("SHARE", Token.SHARE);
      map.put("KEY", Token.KEY);
      map.put("TEMPORARY", Token.TEMPORARY);
      map.put("TEMP", Token.TEMP);
      map.put("UNLOGGED", Token.UNLOGGED);
      map.put("CONFLICT", Token.CONFLICT);
      map.put("NOTHING", Token.NOTHING);
      map.put("OVERRIDING", Token.OVERRIDING);
      map.put("SYSTEM", Token.SYSTEM);
      map.put("USER", Token.USER);
      DEFAULT_KINGBASE_KEYWORDS = new Keywords(map);
   }
}
