package Package;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class LA {

    private int currentPoint = -1;
    private int currentState = 0;

    private String code;

    private final char[] letters = {
      'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
      't','u','v','w','x','y','z',
      'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R',
      'S','T','U','V','W','X','Y','Z'
    };
    private final char[] digits = {'0','1','2','3','4','5','6','7','8','9'};
    private final char[] symbols = {'(',')','{','}',';','+','-','*','/','=','.','<','>',
    '!','"'
    };
    private final char[] whiteSpaces = {'\n','\t',' '};

    private final String[] keywords = {"ashar","sahih","chap","agar","begir"};

    public Token nextToken(){
        char ch;
        currentState = 0;
        String token_name ="";
        while(true){
        switch (currentState){
            case 0:
            ch = nextChar();
            token_name = token_name + ch;
            if(isInLetters(ch))
                currentState = 1;
            else if(ch=='+')
                currentState = 2;
            else if(ch == '-')
                currentState = 3;
            else if(isInDigits(ch))
                currentState =4;
            else if(ch == '(')
                currentState = 7;
            else if(ch==')')
                currentState = 8;
            else if(ch == ';')
                currentState = 9;
            else if(ch == '{')
                currentState = 10;
            else if(ch == '}')
                currentState = 11;
            else if(ch == '/')
                currentState = 12;
            else if(ch == '"')
                currentState = 18;
            else if(ch == '*')
                currentState = 20;
            else if(ch == '=')
                currentState = 21;
            else if(ch == '<')
                currentState = 23;
            else if(ch == '>')
                currentState = 25;
            else if(ch == '!')
                currentState = 27;
            else if(ch == '$')
                currentState = 29;
            else if(isInWhiteSpaces(ch))
                currentState = 30;
            else{
                token_name = token_name + "is not defined in Simple Language";
                return new Token(token_name, 0,0 , Token.Token_Type.err, Token.ID_Type.none);
            }
                break;
            case 1:
                if(isInLetters(watchNextChar()) || isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 1;
                }else{
                    if(isInKeywords(token_name)) {
                        if(token_name.equals("ashar")) {
                            return new Token(token_name,0,0, Token.Token_Type.ashar, Token.ID_Type.none);
                        }else if(token_name.equals("sahih")) {
                            return new Token(token_name,0,0, Token.Token_Type.sahih, Token.ID_Type.none);
                        }else if(token_name.equals("chap")) {
                            return new Token(token_name,0,0, Token.Token_Type.chap, Token.ID_Type.none);
                        }else if(token_name.equals("agar")) {
                            return new Token(token_name,0,0, Token.Token_Type.agar, Token.ID_Type.none);
                        }else if(token_name.equals("begir")) {
                            return new Token(token_name,0,0, Token.Token_Type.begir, Token.ID_Type.none);
                        }
                    }
                    else {
                        return new Token(token_name,0,0, Token.Token_Type.id, Token.ID_Type.none);
                    }
                }
                break;
            case 2:
                if(isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 4;
                }else{
                    return new Token(token_name,0,0, Token.Token_Type.plus, Token.ID_Type.none);
                }
                break;
            case 3:
                if(isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 4;
                }else{
                    return new Token(token_name,0,0, Token.Token_Type.minus, Token.ID_Type.none);
                }
                break;
            case 4:
                if(isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 4;
                }else if(watchNextChar() == '.'){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 5;
                }else{
                    return new Token(token_name,0,0, Token.Token_Type.intNumber, Token.ID_Type.none);
                }
                break;
            case 5:
                if(isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name +ch;
                    currentState = 6;
                }else{
                    return new Token(token_name  + "is not Defined in Simple Language",0,0, Token.Token_Type.err, Token.ID_Type.none);
                }
                break;
            case 6:
                if(isInDigits(watchNextChar())){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 6;
                }else{
                    return new Token(token_name ,0,0, Token.Token_Type.floatNumber, Token.ID_Type.none);
                }
                break;
            case 7:
                return new Token(token_name ,0,0, Token.Token_Type.leftParentheses, Token.ID_Type.none);
            case 8:
                return new Token(token_name ,0,0, Token.Token_Type.rightParentheses, Token.ID_Type.none);
            case 9:
                return new Token(token_name ,0,0, Token.Token_Type.semicolon, Token.ID_Type.none);
            case 10:
                return new Token(token_name ,0,0, Token.Token_Type.leftAccolade, Token.ID_Type.none);
            case 11:
                return new Token(token_name ,0,0, Token.Token_Type.rightAccolade, Token.ID_Type.none);
            case 12:
                if(watchNextChar() == '/'){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState =13;
                }else if(watchNextChar() == '*'){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 15;
                }else{
                    return new Token(token_name ,0,0, Token.Token_Type.divide, Token.ID_Type.none);
                }
                break;
            case 13:
                if(watchNextChar() == '$')
                    return new Token(token_name ,0,0, Token.Token_Type.lineComment, Token.ID_Type.none);
                else if(watchNextChar() == '\n'){
                    nextChar();
                    currentState = 14;
                }else {
                    ch = nextChar();
                    token_name = token_name +ch;
                    currentState =13;
                }
                break;
            case 14:
                return new Token(token_name ,0,0, Token.Token_Type.lineComment, Token.ID_Type.none);
            case 15:
                if(watchNextChar() == '$'){
                    return new Token("Multi Line Comment Missing */",0,0,Token.Token_Type.err, Token.ID_Type.none);
                }
                else if(watchNextChar() == '*'){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 16;
                }else{
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 15;
                }
                break;
            case 16:
                if(watchNextChar() == '$'){
                    return new Token("Multi Line Comment Missing */",0,0,Token.Token_Type.err, Token.ID_Type.none);
                }
                else if(watchNextChar() == '/') {
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 17;
                }
                else if (watchNextChar() == '*') {
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 16;
                }
                else{
					ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 15;
				}
                break;
            case 17:
                return new Token(token_name ,0,0, Token.Token_Type.multiLineComment, Token.ID_Type.none);
            case 18:
                if(watchNextChar() == '$'){
                    return new Token("String Missing \"",0,0,Token.Token_Type.err, Token.ID_Type.none);
                }
                else if(watchNextChar() == '"'){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 19;
                }
                else{
                ch = nextChar();
                token_name = token_name + ch;
                currentState = 18;
                }
                break;
            case 19:
                return new Token(token_name ,0,0, Token.Token_Type.String, Token.ID_Type.none);
            case 20:
                return new Token(token_name ,0,0, Token.Token_Type.multiple, Token.ID_Type.none);
            case 21:
                if(watchNextChar() == '='){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState =22;
                }else{
                    return new Token(token_name ,0,0, Token.Token_Type.equal, Token.ID_Type.none);
                }
                break;
            case 22:
                return new Token(token_name ,0,0, Token.Token_Type.equalTwice, Token.ID_Type.none);
            case 23:
                if(watchNextChar() == '='){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 24;
                }else{
                    return new Token(token_name ,0,0, Token.Token_Type.less, Token.ID_Type.none);
                }
                break;
            case 24:
                return new Token(token_name ,0,0, Token.Token_Type.lessEqual, Token.ID_Type.none);
            case 25:
                if (watchNextChar() == '=') {
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 26;
                }else{
                    return new Token(token_name ,0,0, Token.Token_Type.more, Token.ID_Type.none);
                }
                break;
            case 26:
                return new Token(token_name ,0,0, Token.Token_Type.moreEqual, Token.ID_Type.none);
            case 27:
                if(watchNextChar() == '='){
                    ch = nextChar();
                    token_name = token_name + ch;
                    currentState = 28;
                }else{
                    return new Token("not (!=) Missing =",0,0, Token.Token_Type.err, Token.ID_Type.none);
                }
                break;
            case 28:
                return new Token(token_name ,0,0, Token.Token_Type.notEqual, Token.ID_Type.none);
            case 29:
                return new Token(token_name ,0,0, Token.Token_Type.$, Token.ID_Type.none);
            case 30:
                token_name = "";
                currentState = 0;
                break;
            }
        }
    }

    //-----------------------------------nextChar
    private char nextChar(){
        char ch;
        currentPoint++;

        if(currentPoint >= code.length())
            return '$';

        ch = code.charAt(currentPoint);

        if(!(isInDigits(ch) || isInLetters(ch) || isInSymbols(ch) || isInWhiteSpaces(ch))){
            System.err.println("character " + ch + " is not in Alphabet");
            ch=' ';
        }

        return ch;
    }

    private char watchNextChar(){
        char ch;
        int watchNextCharPoint = currentPoint +1;

        if(watchNextCharPoint >= code.length())
            return '$';

        ch = code.charAt(watchNextCharPoint);

        return ch;
    }

    //------------------------------------Alphabet
    private boolean isInLetters(char ch){
        for (char letter : letters) {
            if (letter == ch)
                return true;
        }
        return false;
    }

    private boolean isInWhiteSpaces(char ch){
        for (char whiteSpace : whiteSpaces) {
            if (whiteSpace == ch)
                return true;
        }
        return false;
    }

    private boolean isInDigits(char ch){
        for (char digit : digits) {
            if (digit == ch)
                return true;
        }
        return false;
    }

    private boolean isInSymbols(char ch){
        for (char symbol : symbols) {
            if (symbol == ch)
                return true;
        }
        return false;
    }

    private boolean isInKeywords(String word){
        for (String keyword : keywords) {
            if (word.equals(keyword))
                return true;
        }
        return false;
    }

    //----------------------------------------Constructor
    public LA(String path){
        this.setPath(path);
    }

    //------------------------------------------SetPath
    private void setPath(String path) {
        code ="";
        try {
            File source = new File(path);
            Scanner in = new Scanner(source);
            while(in.hasNextLine()){
                code = code.concat("\n").concat(in.nextLine());
            }
            in.close();
        }catch (FileNotFoundException e){
            System.err.println("Source File not Found");
            e.printStackTrace();
        }
    }
}
