package org.sunflow.system;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class Parser {
    private FileReader file;
    private BufferedReader bf;
    private String[] lineTokens;
    private int index;

    public Parser(String filename) throws FileNotFoundException {
        file = new FileReader(filename);
        bf = new BufferedReader(file);
        lineTokens = new String[0];
        index = 0;
    }

    public void close() throws IOException {
        if (file != null)
            file.close();
        bf = null;
    }

    public String getNextToken() throws IOException {
        while (true) {
            String tok = fetchNextToken();
            if (tok == null)
                return null;
            if (tok.equals("/*")) {
                do {
                    tok = fetchNextToken();
                    if (tok == null)
                        return null;
                } while (!tok.equals("*/"));
            } else
                return tok;
        }
    }

    public boolean peekNextToken(String tok) throws IOException {
        while (true) {
            String t = fetchNextToken();
            if (t == null)
                return false; // nothing left
            if (t.equals("/*")) {
                do {
                    t = fetchNextToken();
                    if (t == null)
                        return false; // nothing left
                } while (!t.equals("*/"));
            } else if (t.equals(tok)) {
                // we found the right token, keep parsing
                return true;
            } else {
                // rewind the token so we can try again
                index--;
                return false;
            }
        }
    }

    private String fetchNextToken() throws IOException {
        if (bf == null)
            return null;
        while (true) {
            if (index < lineTokens.length)
                return lineTokens[index++];
            else if (!getNextLine())
                return null;
        }
    }

    private boolean getNextLine() throws IOException {
        String line = bf.readLine();

        if (line == null)
            return false;

        ArrayList<String> tokenList = new ArrayList<String>();
        String current = new String();
        boolean inQuotes = false;

        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            if (current.length() == 0 && (c == '%' || c == '#'))
                break;

            boolean quote = c == '\"';
            inQuotes = inQuotes ^ quote;

            if (!quote && (inQuotes || !Character.isWhitespace(c)))
                current += c;
            else if (current.length() > 0) {
                tokenList.add(current);
                current = new String();
            }
        }

        if (current.length() > 0)
            tokenList.add(current);
        lineTokens = tokenList.toArray(new String[0]);
        index = 0;
        return true;
    }

    public String getNextCodeBlock() throws ParserException, IOException {
        // read a java code block
        String code = new String();
        checkNextToken("<code>");
        while (true) {
            String line;
            try {
                line = bf.readLine();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            if (line.trim().equals("</code>"))
                return code;
            code += line;
            code += "\n";
        }
    }

    public boolean getNextBoolean() throws IOException {
        return Boolean.valueOf(getNextToken()).booleanValue();
    }

    public int getNextInt() throws IOException {
        return Integer.parseInt(getNextToken());
    }

    public float getNextFloat() throws IOException {
        return Float.parseFloat(getNextToken());
    }

    public void checkNextToken(String token) throws ParserException, IOException {
        String found = getNextToken();
        if (!token.equals(found)) {
            close();
            throw new ParserException(token, found);
        }
    }

    @SuppressWarnings("serial")
    public static class ParserException extends Exception {
        private ParserException(String token, String found) {
            super(String.format("Expecting %s found %s", token, found));
        }
    }
}