package rpn;

import java.net.*;
import java.awt.*;
import rpn.basic.Item;
import rpn.graphics.CalculatorCanvas;

public class CalculatorPanel extends Panel {
	TextArea inputArea, outputArea;
	Button runButton, stopButton, stepButton, 
		resetButton, stackButton, gfxButton;
	PanelCalculator ca;
	TextField instField;
	Color grey = new Color(204, 204, 204);
	Color blue = new Color(204, 255, 255);
	Font labelfont = new Font("Helvetica", Font.BOLD, 16);
	Font figFont = new Font("Courier", Font.BOLD, 16);
	StackFrame stackFrame;
	public GraphicsFrame gfxFrame;
	ItemInterpreter it;
	ItemInstaller ci;
	String title;
	URL loadBase = null;
	
	public CalculatorPanel(int r, String s, String t, 
			ItemInterpreter it, ItemInstaller ci,
			URL loadBase) {
		this.it = it;
		this.ci = ci;
		this.loadBase = loadBase;
		title = t;
		setLayout(new BorderLayout(0, 0));
		setFont(figFont);
		setBackground(grey);
		inputArea = new TextArea(s, r, 60);
		inputArea.setEditable(true);
		inputArea.setBackground(Color.white);
		inputArea.setForeground(Color.black);
		outputArea = new TextArea();
		outputArea.setEditable(false);
		outputArea.setBackground(blue);
		
		runButton = new Button("Run");
		stopButton = new Button("Stop");
		resetButton = new Button("Reset");
		stepButton = new Button("Step");
		stackButton = new Button("Stack");
		gfxButton = new Button("Graphics");
		runButton.setFont(labelfont);
		stopButton.setFont(labelfont);
		stepButton.setFont(labelfont);
		resetButton.setFont(labelfont);
		stackButton.setFont(labelfont);
		gfxButton.setFont(labelfont);
		
		// - now add panels ----------------------
		
		Panel topPanel;
		// top panel: label title + inputArea + text fields 
		topPanel = new Panel();
		topPanel.setLayout(new BorderLayout(0, 0));
		Label titleLabel = new Label(t);
		titleLabel.setFont(labelfont);
		Panel titlePanel = new Panel();
		titlePanel.setLayout(new FlowLayout());
		titlePanel.add(new Panel());
		titlePanel.add(titleLabel);
		titlePanel.add(new Panel());
		topPanel.add("North", titlePanel);
		// inputComponent = (Component) inputArea;
		topPanel.add("Center", inputArea);
		
		topPanel.add("South", middlePanel());
		
		add("North", topPanel);
		
		Panel bottomPanel = new Panel();
		bottomPanel.setLayout(new BorderLayout(0, 0));
		Panel buttonPanel = new Panel();
		buttonPanel.setLayout(new GridLayout(1, 6));
		buttonPanel.add(runButton);
		buttonPanel.add(stopButton);
		buttonPanel.add(resetButton);
		buttonPanel.add(stepButton);
		buttonPanel.add(stackButton);
		buttonPanel.add(gfxButton);
		bottomPanel.add("North", buttonPanel);
		bottomPanel.add("Center", outputArea);
		add("Center", bottomPanel);
		
		caConstruct();
		init();
	}
	
	Panel middlePanel() {
		Panel instPanel = new Panel();
		instPanel.setLayout(new FlowLayout());
		int W = 26;
		instField = new TextField(W);
		instField.setBackground(grey);
		instField.setEditable(false);
		instField.setFont(figFont);
		Label instLabel = new Label("Current instruction: ");
		instLabel.setFont(labelfont);
		instPanel.add(instLabel);
		instPanel.add(instField);
		return(instPanel);
	}
	
	// - external events --------------------------------------
	
	/* 
	   1. events in handleEvent 
	   2. ca finish
	   3. other ca instructions like "stop" or output
	   4. stack frame icon/deicon
	   5. applet closing/opening 
	   6. applet running it with a string input
	*/
	
	// - parent -----------------------------------------------
	
	public void parentStart(String initString) {
		if (sfOpen && stackFrame == null) {
			frameInit();
		}
		setInput(initString);
	}
	
	public String parentStop() {
		if (stackFrame != null) {
			frameDestroy();
		}
		if (gfxFrame != null) {
			gfxDestroy();
		}
		ca.stop();
		return(getInput());
	}
	
	public String parentDestroy() {
		if (stackFrame != null) {
			frameDestroy();
		}
		if (gfxFrame != null) {
			gfxDestroy();
		}
		ca.stop();
		return(getInput());
	}
	
	public synchronized void setInput(String s) {
		 inputArea.setText(s);
	}
	
	public synchronized String getInput() {
		return(inputArea.getText());
	}
	
	public String getOutput() {
		return(outputArea.getText());
	}
	
	public String run(String s) {
		// run the string s as a program
		setInput(s);
		init();
		ca.start();
		while (ca.running) {
			try {
				Thread.sleep(ca.sleepTime);
			} catch (InterruptedException ex) { ; }
		}
		return(outputArea.getText());
	}
	
	public void frameInit() {
		stackFrame = new StackFrame(this, title);
		displayStack();
	}
	
	public void frameDestroy() {
		sfOpen = false;
		stackButton.setBackground(grey);
		stackButton.setForeground(Color.black);
		stackFrame = null;
	}
	
	public void gfxInit() {
		gfxFrame = new GraphicsFrame(this, title + " graphics", 
				(int) (0.64*20*72/2.54), (int)(0.64*20*72/2.54));
	}
	
	public void gfxDestroy() {
		gfxOpen = false;
		gfxButton.setBackground(grey);
		gfxButton.setForeground(Color.black);
		gfxFrame = null;
	}
	
	// - stack frame --------------------------------------
	
	// Three states of the stack frame
	// open and not iconified
	// open but iconified
	// not open
	
	boolean sfIconified = false;
	public boolean sfOpen = false;
	public boolean gfxOpen = false;
	
	void sfIcon() {
		sfIconified = true;
	}
	
	void sfDeicon() {
		sfIconified = false;
	}
	
	// - calculator ---------------------------------------
	
	// called by "stop"
	void caStop() {
		dim(stopButton);
		brighten(runButton);
		brighten(stepButton);
		brighten(resetButton);
		displayStack();
	}
	
	public void caFinish() {
		displayStack();
		instField.setBackground(Color.gray);
		instField.setText("");
		brighten(resetButton);
		dimButtons();
	}
	
	void caOutput(String s) {
		append(s + "\n");
	}
		
	// ----------------------------------------------------
	
	int count = 0;
	boolean dirtyText = false;
	public boolean handleEvent(Event evt) {
		if (evt.target == inputArea) {
			// a mouse click or a key pressed in the source window?
			if (evt.id == Event.GOT_FOCUS || evt.id == Event.KEY_PRESS) {
				setDirty();
			}
		} 
		if (evt.target == runButton && evt.id == Event.ACTION_EVENT) {
			if (!ca.running) {	
				if (ca == null || dirtyText) {
					init();
				}
				dim(runButton);
				dim(stepButton);
				dim(resetButton);
				brighten(stopButton);
				ca.start();
			}
		} else if (evt.target == stopButton && evt.id == Event.ACTION_EVENT) {
			if (ca != null && ca.running) {
				ca.stop();
				displayStack();
				dim(stopButton);
				brighten(stepButton);
				brighten(runButton);
				brighten(resetButton);
			}
		} else if (evt.target == resetButton && evt.id == Event.ACTION_EVENT) {
			if (ca != null && !ca.running) {
				init();
				if (gfxFrame != null) gfxFrame.reset();
			} /* else {
				dim(stopButton);
				dim(resetButton);
			} */
		} else if (evt.target == stackButton && evt.id == Event.ACTION_EVENT) {
			if (stackFrame == null) {
				frameInit();
				sfOpen = true;
				stackButton.setBackground(Color.gray);
				stackButton.setForeground(Color.white);
			} else {
				stackFrame.dispose();
				frameDestroy();
			}
		} else if (evt.target == gfxButton && evt.id == Event.ACTION_EVENT) {
			if (gfxFrame == null) {
				gfxInit();
				gfxOpen = true;
				gfxButton.setBackground(Color.gray);
				gfxButton.setForeground(Color.white);
			} else {
				gfxFrame.dispose();
				gfxDestroy();
			}
		} else if (evt.target == stepButton && evt.id == Event.ACTION_EVENT) {
			if (ca == null || dirtyText) {
				init();
			} 
			Item it = ca.step();
			if (it == null) {
				caFinish();
			} else {
				displayStack();
			}
			brighten(resetButton);
		} 
		return(super.handleEvent(evt));
	}
	
	void dim(Button b) {
		b.setBackground(grey);
		b.setForeground(Color.gray);
	}
	
	void setDirty() {
		dirtyText = true;
		outputArea.setForeground(Color.gray);
		if (stackFrame != null) {
			stackFrame.setTextColor(Color.gray);
		}
		instField.setBackground(grey);
		instField.setText("");
		dim(stopButton);
		brighten(runButton);
		brighten(stepButton);
		dim(resetButton);
	}
	
	void setClean() {
		dirtyText = false;
		outputArea.setForeground(Color.black);
		if (stackFrame != null) {
			stackFrame.setTextColor(Color.black);
		}
	}
	
	void dimButtons() {
		dim(runButton);
		dim(stepButton);
		dim(stopButton);
	}
	
	void brighten(Button b) {
		b.setBackground(grey);
		b.setForeground(Color.black);
	}
	
	public void displayStack() {
		if (ca != null) {
			if (stackFrame != null) {
				stackFrame.setText(ca.stackToString());
			}
			if (ca.it != null) {
				instField.setText(ca.it.toString());
			}
		} 
	}
	
	public void append(String s) {
		outputArea.appendText(s);
	}
	
	void resetButtons() {
	}
	
	void caConstruct() {
		ca = new PanelCalculator(this, loadBase, it);
		ci.install(ca);
	}
	
	public void init() {
		setClean();
		outputArea.setText("");
		outputArea.setForeground(Color.black);
		instField.setText("");
		instField.setBackground(grey);
		if (stackFrame != null) {
			stackFrame.setText("");
		}
		
		brighten(runButton);
		brighten(stepButton);
		dim(stopButton);
		dim(resetButton);
		
		// It is very inefficient to construct a new calculator ...
		if (ca != null) {
			ca.stop();
			caConstruct();
		} else {
			caConstruct();
		}
		// and a new input as well!
		ItemInput ip = new ItemInput(it);
		ip.setCalculator(ca);
		StringInput in = new StringInput(getInput());
		ip.setInput(in);
		ca.setItemStream(ip);
		// We should be able just to reset ca to input in.
	}
	
}
