/**************************************************************************
  * MiniPro2: Interpreter for version 2 of a miniature programming language.
  * 
  * COS 126, Princeton University, Fall 2013, Programming Midterm 2 Part 1
  * 
  * Notes: functionally different from MiniPro in step() and isDone();
  * has private helper method evaluate(), not part of API.
  * 
  * Dependencies: ST, StdOut
  * 
  * Compilation: javac-introcs MiniPro.java
  **************************************************************************/
public class MiniPro2 {
    private int pc;                       // the program counter
    private String[][] program;           // the program itself
    private ST<String, Integer> varTable; // values of all defined variables
    
    // Create interpreter for this program. Don't execute it yet!
    public MiniPro2(String[][] program) {
        this.program = program;
        pc = 0; // line 0 is always the first to execute
        varTable = new ST<String, Integer>();
    }

    // Return the current value of the variable named v. If no
    // such variable is currently defined, throw a RuntimeException.
    public int valueOf(String v) {
        if (!varTable.contains(v))
            throw new RuntimeException("Variable named " + v + " not defined");
        return varTable.get(v);
    }

    // Return the number of the line that will execute next.
    public int programCounter() {
        return pc;
    }
    
    // Check if this token is an integer or variable, and give the value
    // of it in either case.
    private int evaluate(String token) {
        // look at token, evaluate it
        if (token.matches("[a-z]+"))       // it's a variable name
            return varTable.get(token);
        else                               // it's an integer
            return Integer.parseInt(token);
    }

    // Execute the line whose number equals the value of the
    // program counter. Then, increment the program counter.
    public void step() {
        String[] line = program[pc]; // current line (1d piece of 2d array)
        String command = line[1]; 

        // assignment statement
        if (command.equals("=")) {
            // look at token on right-hand side, evaluate it
            String rhsToken = line[2];
            int rhsValue = evaluate(rhsToken);
            
            // save value in variable
            varTable.put(line[0], rhsValue);
        }

        // println statement
        else if (command.equals("println")) {
            // get value of desired variable, then print it
            int value = varTable.get(line[0]);
            StdOut.println(value);
        }

        // mathematical operations
        else if (command.equals("+=")) {
            // which variable are we updating?
            int oldValue = varTable.get(line[0]);

            // what should its new value be?
            int newValue = oldValue + evaluate(line[2]);
            
            // update
            varTable.put(line[0], newValue);
        }
        else if (command.equals("-=")) {
            // which variable are we updating?
            int oldValue = varTable.get(line[0]);

            // what should its new value be?
            int newValue = oldValue - evaluate(line[2]);
            
            // update
            varTable.put(line[0], newValue);
        }
        else if (command.equals("*=")) {
            // which variable are we updating?
            int oldValue = varTable.get(line[0]);

            // what should its new value be?
            int newValue = oldValue * evaluate(line[2]);
            
            // update
            varTable.put(line[0], newValue);
        }

        // jump if positive statement
        else if (command.equals("pos?jump")) {
            // value of variable we are testing
            int testValue = evaluate(line[0]);
            
            if (testValue > 0) { // positive?
                pc += evaluate(line[2]); // jump!
                // return right now so that we don't hit the pc++ line below
                return;
            }
            // else, take no special action
        }
        
        // increment the program counter
        pc++;
    }

    // Is the program done?
    // For MiniPro2, we have to check not only being at the end,
    // but also after the end or before the start.
    public boolean isDone() {
        return pc >= program.length || pc < 0;
    }

}