import java.io.IOException;
import java.io.EOFException;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;

public class Tokenizer {
    protected Reader r;
    protected Token previousToken;
    protected char c;
    protected StringBuffer tokenData;
    protected boolean eof;

    public Tokenizer(Reader r) {
	this.r = r;
	previousToken = null;
	tokenData = new StringBuffer(100);
	eof = false;
	read();
    }

    protected void read() {
	try {
	    int i = r.read();
	    if (i == -1) {
		eof = true;
	    } else {
		c = (char)i;
	    }
	} catch(IOException e) {
	    eof = true;
	}
    }

    protected void skipWhitespace() {
	while (!eof && Character.isWhitespace(c)) {
	    read();
	}
    }

    protected void readIdentifier() {
	tokenData.setLength(0);
	tokenData.append(c);
	read();
	while (!eof && Character.isLetterOrDigit(c)) {
	    tokenData.append(c);
	    read();
	}
    }

    protected void readString() {
	tokenData.setLength(0);
	read();
	while (!eof && c != '"') {
	    tokenData.append(c);
	    read();
	}
	if (eof) {
	    System.err.println("Syöte loppui merkkijonon sisällä");
	} else {
	    read();
	}
    }

    protected Token readToken() {
	while (true) {
	    skipWhitespace();
	    if (eof) {
		return null;
	    } else if (c == '(') {
		read();
		return new Token(Token.LEFT_PAREN, null);
	    } else if (c == ')') {
		read();
		return new Token(Token.RIGHT_PAREN, null);
	    } else if (c == '=') {
		read();
		return new Token(Token.ASSIGN, null);
	    } else if (c == ',') {
		read();
		return new Token(Token.COMMA, null);
	    } else if (c == ';') {
		read();
		return new Token(Token.SEMICOLON, null);
	    } else if (Character.isLetter(c)) {
		readIdentifier();
		return new Token(Token.IDENTIFIER, tokenData.toString());
	    } else if (c == '"') {
		readString();
		return new Token(Token.STRING, tokenData.toString());
	    } else {
		read();
		return new Token(Token.UNKNOWN, null + c);
	    }
	}
    }

    public Token getToken() {
	if (previousToken != null) {
	    Token aux = previousToken;
	    previousToken = null;
	    return aux;
	} else {
	    return readToken();
	}
    }

    public Token peekToken() {
	if (previousToken == null) {
	    previousToken = readToken();
	}
	return previousToken;
    }

    public void testRead() {
	while (!eof) {
	    System.out.println("Char: " + c + " = " + (int)c);
	    read();
	}
	System.out.flush();
    }

    public void testSkipWhitespace() {
	while (!eof) {
	    skipWhitespace();
	    if (eof) {
		break;
	    }
	    System.out.println("Char: " + c + " = " + (int)c);
	    read();
	}
	System.out.flush();
    }

    public void testReadIdentifier() {
	while (!eof) {
	    skipWhitespace();
	    if (eof) {
		break;
	    }
	    readIdentifier();
	    System.out.println("Identifier: " + tokenData);
	}
	System.out.flush();
    }

    public void testReadString() {
	while (!eof) {
	    skipWhitespace();
	    if (eof) {
		break;
	    }
	    if (c == '"') {
		readString();
		System.out.println("String: " + tokenData);
	    } else {
		System.err.println("Internal error in test");
		System.exit(1);
	    }
	}
	System.out.flush();
    }

    public void testReadToken() {
	while (!eof) {
	    Token t = readToken();
	    System.out.println("Token: " + t);
	}
	System.out.flush();
    }
}
