package rpn;

import java.io.*;
import java.util.Stack;
import java.util.Hashtable;
import rpn.basic.Item;
import rpn.basic.CalcErr;
import rpn.basic.LoadItem;
import rpn.basic.ErrItem;

public class Calculator {
	// operator stack: RPN operators and data to be processed
	Stack opStack;
	// execution stack: item streams
	Stack execStack;
	// input stack: open character streams
	Stack inputStack;
	// dictionary stack: for local variable handling
	Stack dictStack;
	/* [ locations: stack of buried '['
	Stack arrayStack;
	// { locations: stack of buried '{'
	Stack procStack; */
	public ItemInterpreter it;
	
	public Hashtable systemDict, userDict;
	public int procDepth = 0;
	public final static int Fix = 0, Sci = 1;
	public int fix = 4;
	public int mode = Fix;
	public String name = "";

	public Hashtable pgmDict = null;
	
	public Calculator(ItemInterpreter it) {
		this.it = it;
		opStack = new Stack();
		execStack = new Stack();
		inputStack = new Stack();
		dictStack = new Stack();
		pgmDict = new Hashtable();
		/* arrayStack = new Stack();
		procStack = new Stack(); */
		// initialize with system dictionary
		// user dictionary, standard input
	 	systemDict = new Hashtable();
	 	dictStack.push(systemDict);
	 	userDict = new Hashtable();
	 	dictStack.push(userDict);
	}
	
	// ---------------------------------------------
	
	public void pause() { ; }
	
	public void pgmInstall(String key, String value) {
		pgmDict.put(key, value);
	}
	
	public String pgmGet(String key) {
		return((String) pgmDict.get(key));
	}
	
	// maintain a 'current directory' 
	// and have file names relative to that?
	public String load(String s) {
		try {
			FileInputStream fis = new FileInputStream(s);
			StringBuffer sb = new StringBuffer();
			int c = fis.read();
			while (c != -1) {
				sb.append((char) c);
				c = fis.read();
			}
			return(new String(sb));
			
		} catch(FileNotFoundException e) { 
			return(null);
		} 
		catch(IOException e) { 
			return(null);
		} 
	} 
	
	/* public String load(String s) {
		return(pgmGet(s));
	} */

	// - execution stack ---------------------------
	
	public int execSize() {
		return(execStack.size());
	}

	public void setItemStream(ItemStream is) {
		execStack.push(is);
	}
	
	public void pushExec(ItemStream is) {
		execStack.push(is);
	}
	
	public ItemStream popExec() {
		return((ItemStream) execStack.pop());
	}
	
	public Item getItem() {
		while (execStack.size() > 0) {
			ItemStream is = (ItemStream) execStack.peek();
			if (!is.atEOF()) {
				Item it = is.getItem();
				return(it);
			} else {
				execStack.pop();
			}
		}
		return(null);
	}
	
	public ItemStream currentItemStream() {
		return((ItemStream) execStack.peek());
	}
	
	// - for the terminal version ---------------------
	
	public void output(Item it) {
		System.out.println(it);
	}
	
	// - operator stack ------------------------------- 
	
	public boolean check(int n) {
		if (opStack.size() < n) {
			return(false);
		} else return(true); 
	}
	
	public int size() {
		return(opStack.size());
	}
	
	public Item peek() {
		int n = opStack.size();
		Item it = (Item) opStack.elementAt(n-1);
		return(it);
	}

	public Item item(int i) {
		Item it = (Item) opStack.elementAt(i);
		return(it);
	}
	
	public Item peek(int i) {
		int n = opStack.size();
		Item it = (Item) opStack.elementAt(n-1-i);
		return(it);
	}
	
	/* public Item element(int i) {
		Item it = (Item) opStack.elementAt(i);
		return(it);
	} */
	
	public void push(Item it) {
		opStack.push(it);
	}
	
	public void pop(int n) {
		for (int i=0;i<n;i++) {
			opStack.pop();
		}
	}
	
	public Item pop() {
		return((Item) opStack.pop());
	}
	
	// - dict stack --------------------------------
	
	public void systemInstall(Item it) {
		systemDict.put(it.toString(), it);
	}
	
	public void install(Item it) {
		currentDict().put(it.toString(), it);
	}
	
	public int dictStackSize() {
		return(dictStack.size());
	}
	
	public Hashtable currentDict() {
		int N = dictStack.size();
		return((Hashtable) dictStack.elementAt(N-1));
	}
	
	public Hashtable dictAt(int n) {
		int N = dictStack.size();
		return((Hashtable) dictStack.elementAt(n));
	}
	
	public Hashtable dictPeek(int n) {
		int N = dictStack.size();
		return((Hashtable) dictStack.elementAt(N-n-1));
	}
	
	public void localDict() {
		Hashtable h = new Hashtable();
		// System.out.println("push 1: dict size = " + dictStackSize());
		dictStack.push(h);
		// System.out.println("push 2: dict size = " + dictStackSize());
	}
	
	public void endDict() {
		// System.out.println("pop 1: dict size = " + dictStackSize());
		dictStack.pop();
		// System.out.println("pop 2: dict size = " + dictStackSize());
	}
	
	public Item lookup(String s) {
		int n = dictStackSize();
		Item x = null;
		// System.out.println("lookup: dict size = " + dictStackSize());
		while (x == null && n > 0) {
			// System.out.println("lookup: n = " + (n-1));
			Hashtable h = dictAt(--n);
			x = (Item) (h.get(s));
			// System.out.println("x = " + x);
		}
		// System.out.println("-------");
		return(x);
	}
	
	// ---------------------------------------------
	
	public void error(Item it, int n) {
		if (!(it instanceof ErrItem)) {
			output(new ErrItem(this, it, n));
		} else {
			output(it);
		}
	}
	
	public String stackToString() {
		StringBuffer s = new StringBuffer();
		for (int i=0;i<opStack.size();i++) {
			Item it = (Item) opStack.elementAt(i);
			s.append(it.toString());
			s.append('\n');
		}
		return(new String(s));
	}

	public String toString() {
		String s = new String();
		for (int i=0;i<opStack.size();i++) {
			Item it = (Item) opStack.elementAt(i);
			s += it.toString();
			s += "\n";
		}
		return(s);
	}

	public void printExec() {
		for (int i=0;i<execStack.size();i++) {
			System.out.println("- [" + i + "] -----------");
			ItemStream is = (ItemStream) execStack.elementAt(i);
			System.out.println(is);
		}
		System.out.println("====================================");
	}
	public void print() {
		for (int i=0;i<opStack.size();i++) {
			Item it = (Item) opStack.elementAt(i);
			System.out.println(": " + it);
		}
	}
}
