package net.vulkanmod.vulkan.shader.converter;

import java.util.ArrayList;
import java.util.List;

public class Lexer {
   private final String input;
   private int currentPosition;
   private char currentChar;
   private State state;

   public Lexer(String input) {
      this.input = input;
      this.currentPosition = 0;
      this.currentChar = !input.isEmpty() ? input.charAt(0) : 0;
   }

   private void advance() {
      this.advance(1);
   }

   private void advance(int i) {
      for(int j = 0; j < i; ++j) {
         ++this.currentPosition;
         if (this.currentPosition >= this.input.length()) {
            this.currentChar = 0;
            break;
         }

         this.currentChar = this.input.charAt(this.currentPosition);
      }

   }

   private char peek() {
      int peekPosition = this.currentPosition + 1;
      return peekPosition < this.input.length() ? this.input.charAt(peekPosition) : '\u0000';
   }

   public List<Token> tokenize() {
      List<Token> tokens = new ArrayList();

      while(this.currentPosition < this.input.length()) {
         char currentChar = this.input.charAt(this.currentPosition);
         Token token = this.nextToken();
         if (token == null) {
            throw new RuntimeException("Unknown character: " + currentChar);
         }

         tokens.add(token);
      }

      tokens.add(new Token(Token.TokenType.EOF, (String)null));
      return tokens;
   }

   public Token nextToken() {
      if (!this.checkEOF()) {
         return new Token(Token.TokenType.EOF, (String)null);
      } else if (this.currentChar == '/' && this.peek() == '/') {
         this.advance(2);
         return this.comment();
      } else {
         label79:
         switch (this.currentChar) {
            case '!':
               if (this.peek() == '=') {
                  this.advance(2);
                  return new Token(Token.TokenType.OPERATOR, "!=");
               }
               break;
            case '<':
               switch (this.peek()) {
                  case '<':
                     this.advance(2);
                     return new Token(Token.TokenType.OPERATOR, "<<");
                  case '=':
                     this.advance(2);
                     return new Token(Token.TokenType.OPERATOR, "<=");
                  default:
                     break label79;
               }
            case '=':
               if (this.peek() == '=') {
                  this.advance(2);
                  return new Token(Token.TokenType.OPERATOR, "==");
               }
               break;
            case '>':
               switch (this.peek()) {
                  case '=':
                     this.advance(2);
                     return new Token(Token.TokenType.OPERATOR, ">=");
                  case '>':
                     this.advance(2);
                     return new Token(Token.TokenType.OPERATOR, ">>");
               }
         }

         Token var10000;
         switch (this.currentChar) {
            case '!':
               var10000 = new Token(Token.TokenType.OPERATOR, "!");
               break;
            case '"':
               var10000 = this.string();
               break;
            case '#':
               StringBuilder sb = new StringBuilder();

               while(this.checkEOF() && this.currentChar != '\n') {
                  sb.append(this.currentChar);
                  this.advance();
               }

               sb.append('\n');
               String value = sb.toString();
               var10000 = new Token(Token.TokenType.PREPROCESSOR, value);
               break;
            case '$':
            case '\'':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '@':
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
            case 'G':
            case 'H':
            case 'I':
            case 'J':
            case 'K':
            case 'L':
            case 'M':
            case 'N':
            case 'O':
            case 'P':
            case 'Q':
            case 'R':
            case 'S':
            case 'T':
            case 'U':
            case 'V':
            case 'W':
            case 'X':
            case 'Y':
            case 'Z':
            case '\\':
            case '_':
            case '`':
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
            case 'g':
            case 'h':
            case 'i':
            case 'j':
            case 'k':
            case 'l':
            case 'm':
            case 'n':
            case 'o':
            case 'p':
            case 'q':
            case 'r':
            case 's':
            case 't':
            case 'u':
            case 'v':
            case 'w':
            case 'x':
            case 'y':
            case 'z':
            default:
               var10000 = null;
               break;
            case '%':
               var10000 = new Token(Token.TokenType.OPERATOR, "%");
               break;
            case '&':
               var10000 = new Token(Token.TokenType.OPERATOR, "&");
               break;
            case '(':
               var10000 = new Token(Token.TokenType.LEFT_PARENTHESIS, "(");
               break;
            case ')':
               var10000 = new Token(Token.TokenType.RIGHT_PARENTHESIS, ")");
               break;
            case '*':
               var10000 = new Token(Token.TokenType.OPERATOR, "*");
               break;
            case '+':
               var10000 = new Token(Token.TokenType.OPERATOR, "+");
               break;
            case ',':
               var10000 = new Token(Token.TokenType.COMMA, ",");
               break;
            case '-':
               var10000 = new Token(Token.TokenType.OPERATOR, "-");
               break;
            case '.':
               var10000 = new Token(Token.TokenType.DOT, ".");
               break;
            case '/':
               var10000 = new Token(Token.TokenType.OPERATOR, "/");
               break;
            case ':':
               var10000 = new Token(Token.TokenType.COLON, ":");
               break;
            case ';':
               var10000 = new Token(Token.TokenType.SEMICOLON, ";");
               break;
            case '<':
               var10000 = new Token(Token.TokenType.OPERATOR, "<");
               break;
            case '=':
               var10000 = new Token(Token.TokenType.OPERATOR, "=");
               break;
            case '>':
               var10000 = new Token(Token.TokenType.OPERATOR, ">");
               break;
            case '?':
               var10000 = new Token(Token.TokenType.OPERATOR, "?");
               break;
            case '[':
               var10000 = new Token(Token.TokenType.OPERATOR, "[");
               break;
            case ']':
               var10000 = new Token(Token.TokenType.OPERATOR, "]");
               break;
            case '^':
               var10000 = new Token(Token.TokenType.OPERATOR, "^");
               break;
            case '{':
               var10000 = new Token(Token.TokenType.LEFT_BRACE, "{");
               break;
            case '|':
               var10000 = new Token(Token.TokenType.OPERATOR, "|");
               break;
            case '}':
               var10000 = new Token(Token.TokenType.RIGHT_BRACE, "}");
         }

         Token token = var10000;
         if (token == null) {
            if (Character.isLetter(this.currentChar)) {
               return this.identifier();
            }

            if (Character.isDigit(this.currentChar)) {
               return this.literal();
            }

            if (Character.isWhitespace(this.currentChar)) {
               return this.spacing();
            }
         }

         if (token == null) {
            throw new IllegalStateException("Unrecognized char: " + this.currentChar);
         } else {
            this.advance();
            return token;
         }
      }
   }

   private Token comment() {
      StringBuilder sb = new StringBuilder();
      sb.append("//");

      while(this.checkEOF() && this.currentChar != '\n') {
         sb.append(this.currentChar);
         this.advance();
      }

      sb.append(this.currentChar);
      this.advance();
      String value = sb.toString();
      return new Token(Token.TokenType.COMMENT, value);
   }

   private Token identifier() {
      StringBuilder sb = new StringBuilder();

      while(this.checkEOF() && Character.isJavaIdentifierPart(this.currentChar)) {
         sb.append(this.currentChar);
         this.advance();
      }

      String value = sb.toString();
      return new Token(Token.TokenType.IDENTIFIER, value);
   }

   private Token literal() {
      StringBuilder sb = new StringBuilder();

      while(Character.isDigit(this.currentChar)) {
         sb.append(this.currentChar);
         this.advance();
      }

      if (this.currentChar == '.') {
         sb.append(this.currentChar);
         this.advance();
      }

      while(Character.isDigit(this.currentChar)) {
         sb.append(this.currentChar);
         this.advance();
      }

      String value = sb.toString();
      return new Token(Token.TokenType.LITERAL, value);
   }

   private Token string() {
      StringBuilder sb = new StringBuilder();

      while(this.checkEOF() && this.currentChar != '"') {
         sb.append(this.currentChar);
         this.advance();
      }

      sb.append(this.currentChar);
      this.advance();
      String value = sb.toString();
      return new Token(Token.TokenType.COMMENT, value);
   }

   private Token spacing() {
      StringBuilder sb = new StringBuilder();

      while(this.currentChar != 0 && Character.isWhitespace(this.currentChar)) {
         sb.append(this.currentChar);
         this.advance();
      }

      String value = sb.toString();
      return new Token(Token.TokenType.SPACING, value);
   }

   private boolean checkEOF() {
      return this.currentChar != 0;
   }

   static enum State {
      UNIFORM_BLOCK,
      CODE,
      DEFAULT;

      // $FF: synthetic method
      private static State[] $values() {
         return new State[]{UNIFORM_BLOCK, CODE, DEFAULT};
      }
   }
}
