package cmmCompiler;

/**
 * Lexical Analyzer
 */
import java.util.Vector;
import java.io.RandomAccessFile;
import java.io.FileNotFoundException;
import java.io.IOException;

public class LexScan {
	RandomAccessFile in = null;
	long position = 0;
	int lineCount = 0;
	int column=0;
    String line="";
    Token token=null;
    boolean EndOfFile = false;
	public static void main(String[] args)
	{
		LexScan lex = new LexScan("src/cmmCompiler/LexScan.java");
		Vector <Token> sourceCode = lex.scan();
		for (Token token : sourceCode ){
			System.out.println(token);
		}

	}
	public LexScan(String fileName)
	{
        try {//open file
			in = new RandomAccessFile(fileName,"r");
		} catch (FileNotFoundException e) {
			System.out.println("unable to open file");
			e.printStackTrace();
		}
		line = getLine(in);

	}
/* 		
	public  Vector <Token> scan(String fileName)
	{
        Vector <Token> tokens = new Vector<Token>();
        try {//open file
			in = new RandomAccessFile(fileName,"r");
		} catch (FileNotFoundException e) {
			System.out.println("unable to open file");
			e.printStackTrace();
		}
		line = getLine(in);
		while (!EndOfFile) {
			skipBlanks();
			while (skipComment())
				skipBlanks();
			//should have a token here
			position+=column;
			if ( (token = getWord()) == null) //keyword, id, number
				if ( (token = getSeperator()) == null) //punctuation
					if ( (token = getOperator()) == null) //operator
						if (!EndOfFile){
							System.out.println("No lexical rules for ["+line.substring(column)+"], ignoring rest of text line");
							getLine();
							//System.exit(0);
						}
						else {
							token = new Token(Token.EOF, "$", lineCount, column,position);
						}
			if (token != null)
				tokens.add(token);
    	}
		try {
			in.close();
		} catch (IOException e) {
			System.out.println("unable to close");
			e.printStackTrace();
		}
		return tokens;
    }
*/    	
	
	public  Token nextToken(){
     	while (!EndOfFile) {
			skipBlanks();
			while (skipComment())
				skipBlanks();
			//should have a token here
			position+=column;
			if ( (token = getWord()) == null) //keyword, id, number
				if ( (token = getSeperator()) == null) //punctuation
					if ( (token = getOperator()) == null) //operator
						if (!EndOfFile){
							System.out.println("No lexical rules for ["+line.substring(column)+"], ignoring rest of text line");
							getLine();
							return nextToken();
						}
						else { //EOF
							token = new Token(Token.EOF, "$", lineCount, column,position);
						}
			if (token != null)
				return token;
    	}
     	return null; //not reached;
	}

	public  Vector <Token> scan()
	{
        Vector <Token> tokens = new Vector<Token>();
		while (!EndOfFile) 
			tokens.add(nextToken());
		return tokens;
    }		

	private void closeFile(){
		try {
			in.close();
		} catch (IOException e) {
			System.out.println("unable to close");
			e.printStackTrace();
		}
	}
	
	
	static String EOF = "$$$";
	void getLine()
	{
		line = getLine(in);
	}
	String getLine(RandomAccessFile in)
	{
		column=0;
		try {//read a line
			while (true)
			{
				lineCount++;
				position = in.getFilePointer();
				String line = in.readLine();
				if (line == null){
					closeFile();
					EndOfFile = true;
					return EOF;
				}
				if (line.length() > 0) return line;
			}
		} catch (IOException e) {
			System.out.println("unable to read line");
			e.printStackTrace();
			return EOF;
		}
	}
	void skipBlanks(){
		while (!EndOfFile && column >= line.length())
		{
			getLine();
		}
		while (!EndOfFile && Character.isWhitespace(line.charAt(column)))
		{
			column++;
			while (!EndOfFile && column >= line.length())
			{
				getLine();
			}
		}
	}
	void findCommentEnd(){
		while (!EndOfFile && column+1 >= line.length()) getLine();
		while (!EndOfFile)
		{
			if (column+1 >= line.length()) getLine();
			else if (line.charAt(column)=='*' && (line.charAt(column+1) == '/'))
			{
				column+=2;
				return;
			}
			column++;
		}
		System.out.println("Unexpected EOF, Expected (*/)");

	}
	boolean skipComment(){
		if (column+1 >= line.length()) return false;
		if (line.charAt(column)=='/')
		{
			if ((line.charAt(column+1)=='/'))
			{
				line = getLine(in);//line comment
				return true;
			}
			if ((line.charAt(column+1)=='*'))
			{
				column+=2;
				findCommentEnd();//block comment
				return true;
			}
		}
		return false;
	}


	Token getWord()//test for and identify id, number, keyword
	{
		Token token = null;
	//	"boolean",  "break",  "byte",  "case",  "catch",  "char",  "class",  "const",  "continue",
	//	"default",  "do",  "double",  "else",  "enum",  "final",  "finally",  "float",  "for",  "goto",
	//	"if",  "int",  "long",  "new",  "private",  "protected",  "public",  "return",
	//	"short",  "switch",  "this",  "throw",  "throws",  "try",  "void",  "while",
		int start = column;
		if (Character.isDigit(line.charAt(column)))
		{
			//Number
			column++;
			while (column < line.length() && Character.isDigit(line.charAt(column)))
			{
				column++;
			}
			token = new Token(Token.NUMBER, line.substring(start,column), lineCount, start, position);
		}
		else if (Character.isLetter(line.charAt(column)))
		{
			//Number
			column++;
			while (column < line.length() && 
					((Character.isLetter(line.charAt(column))) || Character.isDigit(line.charAt(column))))
			{
				column++;
			}
			token = new Token(Token.ID, line.substring(start,column), lineCount, start, position);
			int low = Token.BOOLEAN;
			int high = Token.WHILE+1;
			while (high-low > 0){
				int keyword = (low+high)/2;
				int cmp = token.text.compareTo(Token.tokenSymbol[keyword]);
				if (cmp == 0) {
					token.type = keyword;
					token.text = Token.tokenSymbol[keyword];
					low = high = keyword;
				}
				else if (cmp < 0)
					high = keyword;
				else
					low = keyword+1;
			}			
		}
		return token;
	}
	Token getSeperator()//test for seperator
	{
		Token token;
		int seperator = 0;
	//	"(", ")", "{", "}", "[", "]", ";", ",", ".",
		switch (line.charAt(column))
		{
			case('('):seperator = Token.OPEN_PAREN; break;
			case(')'):seperator = Token.CLOSE_PAREN; break;
			case('{'):seperator = Token.OPEN_CURLY; break;
			case('}'):seperator = Token.CLOSE_CURLY; break;
			case('['):seperator = Token.OPEN_BRACKET; break;
			case(']'):seperator = Token.CLOSE_BRACKET; break;
			case(';'):seperator = Token.SEMI; break;
			case(','):seperator = Token.COMMA; break;
			case('.'):seperator = Token.DOT; break;
			default: return null;
		}
		token = new Token(seperator, Token.tokenSymbol[seperator], lineCount, column, position);
		column++;
		return token;
	}
	Token getOperator()//test for operator
	{
		Token token;
	//"=", ">", "<", "!", "~", "?", ":", "==", "<=", ">=", "!=",
	//"&&", "||", "++", "--",
	//"+", "-", "*", "/", "&", "|", "^", "%", "<<", ">>", ">>>",
	//"+=", "-=", "*=", "/=", "&=", "|=", "^=", "%=",
	//"<<=", ">>=", ">>>="
		int operator = 0;
		char second = (column+1 < line.length())? line.charAt(column+1): ' ';
		char third = (column+2 < line.length())?line.charAt(column+2): ' ';
		//fourth is only required once
		operator = 0;
		switch (line.charAt(column))
		{
			case('?'):
				operator = Token. OP_IIF;    	// ?
				break;
			case(':'): 
				operator = Token.OP_COLON;     // :
				break;
			case('~'): 
				operator = Token.OP_BITCOMP;     // ~
				break;
			case('='): 
				if (second=='=')
					operator = Token.OP_EQ; 	// ==
				else
					operator = Token. ASSIGN; 	// =
				break;
			case('>'): 
				if (second=='=')
					operator = Token. OP_GE;   	// >=
				else 
					if (second == '>')
						if (third == '>')
							if (column+3 < line.length() && line.charAt(column+3)=='=')
								operator = Token.OP_ROTLEQ; 	// >>>=
							else
								operator = Token.OP_ROTLEFT;  	// >>>
						else
							if (third == '=')
								operator = Token.OP_LSHIFTEQ;   	// >>=
							else
								operator = Token.OP_LSHIFT;   	// >>
					else
						operator = Token.OP_GT    ;     	// >
				break;
			case('<'): 
				operator = Token.OP_LT;        	// <
				if (second=='=')
					operator = Token.OP_LE;  	// <=
				else
					if (second == '<')
						if (third == '=')
							operator = Token.OP_RSHIFTEQ;   	// <<=
						else
							operator = Token.OP_RSHIFT; 	// <<
					else
						operator = Token.OP_LT;      	// <
				break;
			case('!'): 
				if (second=='=')
					operator = Token.OP_NE;  	// !=
				else
					operator = Token. OP_NOT;    	// !
				break;
			case('+'): 
				if (second=='=')
					operator = Token.OP_PLUSEQ;  	// +=
				else 
					if (second == '+') 
						operator = Token.OP_INC;  	// ++
					else
						operator = Token.OP_PLUS;  	// +
				break;
			case('-'): 
				if (second=='=')
				operator = Token.OP_MINUSEQ;   	// -=
				else
					if (second == '-') 
						operator = Token.OP_DEC;  	// --
					else
						operator = Token.OP_MINUS;  	// -
				break;
			case('*'): 
				if (second=='=')
					operator = Token.OP_MULEQ;  	// *=
				else
					operator = Token.OP_MUL;  	// *
				break;
			case('/'): 
				if (second=='=')
					operator = Token.OP_DIVEQ;   	// /=
				else
					operator = Token.OP_DIV;  	// /
				break;
			case('&'): 
				if (second=='=')
					operator = Token.OP_BITANDEQ;  	// &=
				else
					if (second == '&') 
						operator = Token.OP_AND;   	// &&
					else
						operator = Token.OP_BITAND;  	// &
				break;
				case('|'): 
					if (second=='=')
						operator = Token.OP_BITOREQ;   	// |=
					else
						if (second == '&') 
							operator = Token.OP_OR;  	// ||
						else
							operator = Token.OP_BITOR;  	// |
					break;
			case('^'): 
				if (second=='=')
					operator = Token.OP_BITXOREQ;  	// ^=
				else
					operator = Token.OP_BITXOR;   	// ^
				break;
			case('%'): 
				if (second=='=')
					operator = Token.OP_MODEQ;   	// %=
				else
					operator = Token.OP_REM;   	// %
				break;
			default: return null;
		}
		token = new Token(operator, Token.tokenSymbol[operator], lineCount, column, position);
		column+=Token.tokenSymbol[operator].length();
		return token;
	}
	



	
	
}