package Wider;

import java.io.IOException;

/**
 * Rekursiv absteigender Parser für den Eingabestring,
 * benutzt die Klassen Scanner zum einlesen eines Tokens und
 * Calculator zum Berechnen der Werte.
 * Folgende einfache Grammatik (EBNF) wird zum Parsen verwendet:
 *
 * network  ::= <resistor> [ { "+" <resistor> } | { "/" <resistor> } ]
 * resistor ::= ( "(" <network> ")" ) | <integer>
 *
 * @see Scanner
 * @see Calculator
 */
public class Parser implements Tokens {

    int token;
    int resistorcount = 0;
    Scanner scanner = null;
    Calculator calcer = null;

    /**
     * Initialisiert Parser mit Eingabestring, der zu Parsen ist.
     */
    public Parser(String in) throws IOException, WiderException {
        scanner = new Scanner(in);
        calcer = new Calculator();
        token = scanner.getToken();
    }

    /**
     * liefert den Rückgabewert des gesamten Ausdrucks.
     * @return Widerstandswert des gesamten Netzwerk
     */
    public double calc() throws Exception {
        double tmp = network();
        if (resistorcount > 15)
            throw new WiderException("too many resistors");

        if (token != EOF)
            throw new WiderException("unexpected token");
        return tmp;
    }

    /**
     *  entspricht <network> in der Grammatik:
     *  berechnet ein einfaches Netzwerk bestehend aus Widerstandswert und Operatoren
     *
     * @see resistor
     *
     * @return Widerstandswert des Netzwerkes, bzw. einer Ebene des Netzwerkes
     */
    double network() throws Exception {
        double ret = 0.0;
        double[] tmp = new double[15];
        int count = 0;

        tmp[0] = resistor();
        if (token == PLUS) {

            while (token == PLUS) {
                token = scanner.getToken();
                tmp[++count] = resistor();
                if (tmp[count] == 0)
                    throw new WiderException("resistor must be greater than zero");
                if (count > 15)
                    throw new WiderException("too many resistors");
            }

           ret = calcer.calc(tmp, PLUS);

        } else if (token == SLASH) {
            while (token == SLASH) {
                token = scanner.getToken();
                tmp[++count] = resistor();
                if (count > 15)
                    throw new WiderException("too many resistors");
            }
            ret = calcer.calc(tmp, SLASH);
        } else if ((token == EOF) || (token == RPAREN)) {
            ret = tmp[0];
        } else
            throw new WiderException("syntax error");

        return ret;
    }

    /**
     * Berechnet einen Widerstandswert, der entweder aus einem int besteht, oder
     * in Klammern steht. Tritt eine Klammer auf, wird ihr Inhalt wieder mit
     * network() geparst.
     *
     * @see network
     *
     * @return Widerstandswert eines einzelnen Widerstands bzw. einer Klammerebene
     */
    double resistor() throws Exception {
        if (token == INT) {
            token = scanner.getToken();
            resistorcount++;
            return scanner.getValue();

        } else if (token == LPAREN) {
            token = scanner.getToken();
            double tmp = network();
            if (token != RPAREN)
                throw new WiderException("klammer nicht geschlossen");

            token = scanner.getToken();

            return tmp;
        } else
            throw new WiderException("unexpected token");

    }
}