import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * Class that defines the memory space for the SILLY interpreter. 
 *   @author Dave Reed 
 *   @version 1/26/20
 */
public class MemorySpace {
    private HashMap<Token, DataValue> stack;
    private ArrayList<String> heap;
    
    /**
     * Constructs an empty memory space.
     */
    public MemorySpace() {
    	this.stack = new HashMap<Token, DataValue>();
        this.heap = new ArrayList<String>();
    }
    
    /**
     * Declares a variable (without storing an actual value).
     * @param variable the variable to be declared
     * @throws Exception if that variable is already declared in the current scope.
     */
    public void declareVariable(Token variable) throws Exception {
        if (!this.stack.containsKey(variable)) {
            this.stack.put(variable, null);
        }
        else {
            throw new Exception("RUNTIME ERROR: variable " + variable + " already declared");
        }
    }
    
    /**
     * Stores a variable/value in the stack segment.
     *   @param variable the variable name
     *   @param val the value to be stored under that name
     *   @throws Exception if the variable is not already declared.
     */
    public void storeVariable(Token variable, DataValue val) throws Exception {
        if (this.stack.containsKey(variable)) {
            this.stack.put(variable, val);
        }
        else {
            throw new Exception("RUNTIME ERROR: variable " + variable + " not declared");
        }
    }

    /**
     * Retrieves the value for a variable.
     *   @param variable the variable name
     *   @return the value stored under that name
     *   @throws Exception if the variable is not declared and assigned a value.
     */
    public DataValue lookupVariable(Token variable) throws Exception {
        if (this.stack.containsKey(variable)) {
            return this.stack.get(variable);
        }
        else {
            throw new Exception("RUNTIME ERROR: variable " + variable + " not declared");
        }
    }
    
    /**
     * Adds a string constant to the heap.
     *   @param str the string to be stored
     *   @return the address (index) where the string was stored
     */
    public int addToHeap(String str) {
        this.heap.add(str);
        return heap.size()-1;
    }
    
    /**
     * Gets the string constant from the specified address in the heap.
     *   @param address the index in the heap where the string is stored
     *   @return the string constant at that index
     */
    public String getFromHeap(int address) {
        return this.heap.get(address);
    }
}
