/*
 * Canvas.java  
 *
 * Copyright 2001
 * College of Computer Science
 * Northeastern University
 * Boston, MA  02115
 *
 * This software may be used for educational purposes as long as
 * this copyright notice is retained intact at the top of all files.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either in whole
 * or in part without explicit permission.
 *
 * Contact information:
 *   Richard Rasala    rasala@ccs.neu.edu
 *   Viera Proulx      vkp@ccs.neu.edu
 *   Jeff Raab         jmr@ccs.neu.edu
 *   Jennifer McDonald jenimac@ccs.neu.edu
 * 
 * Telephone:          617-373-2462
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import java.util.Stack;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;

public class Canvas extends ScrollableDisplay {
	
	// Constants: font
	protected static final int FONT_SIZE = 18;
	protected Font font = new Font(null, Font.PLAIN, FONT_SIZE);
	
	// Constants: color
	protected static final Color NORMAL_COLOR = Color.black;
	protected static final Color BACKGROUND_COLOR = Color.white;
	
	protected BufferedPanel panel = null;
	protected int DEFAULT_WIDTH = 1;
	protected int DEFAULT_HEIGHT= 1;
	
	// constructor
	public Canvas(int width, int height) {
	
		DEFAULT_WIDTH = width;
		DEFAULT_HEIGHT = height;
	
		panel = new BufferedPanel(width, height, 
								  BACKGROUND_COLOR);
		setDisplay(panel);
		setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
		setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
		
	}
	
	public void growHorizontal() {
	
		int width = panel.getBufferWidth();
		int height = panel.getBufferHeight();
		
		panel.setBufferSize(width*2, height);
	
	}
	
	public void growVertical() {
	
		int width = panel.getBufferWidth();
		int height = panel.getBufferHeight();
		
		panel.setBufferSize(width, height * 2);
	
	}

	 public void reset() {
	
		panel.setBufferSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
		clear();
    }
    
    public void clear() {
    	panel.clearPanel();
    }
    
    // To display tape changes
    public static class TapeDisplay extends Canvas {
    
    	protected PlotMark pointer;
    	protected Tape tape;
    	protected int  pos;
    	
    	public TapeDisplay(int width, int height) {
    		
    		super(width, height);
    		pointer = new PlotMark(PlotMark.WEDGE_N);
    		tape = new Tape(" ");
    		pos = 0;
 
    	}
    	
    	public void setData(Tape t) {	
    		tape = t;
    		sizeToFit();
    	}
    	
    	public void reset() {
    		tape = null;
    		tape = new Tape(" ");
    		pos = 0;
    		super.reset();
    	}
	
		public void animate(boolean tapeRead) {
    
    		super.clear();
    		
    		if(tapeRead)
    			pos++;
    	
    		Graphics2D g = panel.getBufferGraphics();
    	
    		// offset character widths by 20
    		int xOffset = 20;
    		int yOffset = 20;
    
    		// set default color and fonts		
    		g.setPaint(NORMAL_COLOR);
    		g.setFont(font);
    		
    		// draw the tape onto the canvas    	
    		for(int i = 0; i < tape.length(); i++) 
    			g.drawString(tape.read(i).toStringData(),
    					 	(i+1) * xOffset, yOffset);
    		
    		// put the reader arrow at the proper position
    		Point2D.Double markPoint = 
    			new Point2D.Double(xOffset * (pos + 1), yOffset + 2);
    			
    		pointer.mark(g, markPoint);
    	
    		repaint();
   	 	}
   	 	
   	 	// allocates the proper size for the canvas
   	 	public void sizeToFit() {
   	 	
   	 		// make sure buffer is large enough to hold the tape
    		while (shouldGrow(tapeSize()))
    			super.growHorizontal();
   	 	}
   	 	
   	 	public int tapeSize() {
            return (tape.length() * 20);	 	
   	 	}
   	 	
   	 	public boolean shouldGrow(int i) {
   	 		return (i > panel.getBufferWidth());
   	 	}
   	 
   	 } 
   	 
   	 
   	 // To display stack changes
   	 public static class StackDisplay extends Canvas{
   	 
   	 	protected Stack stack;
   	 	
   	 	public StackDisplay(int width, int height) {
   	 		
   	 		super(width, height);
   	 		stack = new Stack();

   	 	}
   	 	
   	 	public void setData(Stack s) {
   	 		stack = s;
   	 	}
   	 	
   	 	public void reset() {
   	 		
   	 		stack = null;
   	 		stack = new Stack();
   	 		super.reset();
   	 	}
   	 
   	 	public void animate(StackClass.Action action) {
    
    		super.clear();
    		sizeToFit();
    		
    		if(action != null)
    			action.doAction(stack);
    
    		Stack temp = (Stack) stack.clone();
    		Graphics2D g = panel.getBufferGraphics();
    	
    		g.setPaint(AutomataApplication.getHighlightColor());
    		g.setFont(font);
    			
    		// draw the stack chars on the canvas
    		int offset = 1;
		
			while(temp.size() > 0) {
		
				String s = ((Symbol)temp.pop()).toStringData();
				g.drawString(s, 5, FONT_SIZE * offset);
				offset++;
			}
    	
    		repaint();
    	}
   	 
   	 	// allocates the proper size for the canvas
   	 	public void sizeToFit() {

    		// make sure buffer is large enough to hold the tape
    		while (shouldGrow(stackSize()))
    			super.growVertical();
   	 	}
   	 	
   	 	public int stackSize() {
   	 		return stack.size() * FONT_SIZE;
   	 	}
   	 
   	 	public boolean shouldGrow(int i) {   	 	
   	 		return ( i > panel.getBufferHeight());
   	 	}
   	 
   	 }
   	 
   	 // To display data changes
   	 public static class DataDisplay extends Canvas {
   	 
   	 	protected Stack path;
   	 	protected PathNode node = null;
   	 	protected int offset = 0;
   	 	
   	 	public DataDisplay(int width, int height) {
   	 		
   	 		super(width, height);
   	 		path = new Stack();
   	 		node = null;
   	 		offset = 0;

   	 	}
   	 	
   	 	public void setData() {}
   	 	
   	 	public void reset() {
   	 	
   	 		path = null;
   	 		path = new Stack();
   	 		node = null;
   	 		offset = 1;
   	 		super.reset();   	 	
   	 	}
   	 	
   	 	public void animate(String s) {
   	 		
   	 		if (s.length() == 0)
   	 			return;
   	 			
   	 		sizeToFit();
   	 	
    		Graphics2D g = panel.getBufferGraphics();
    	
    		g.setPaint(AutomataApplication.getHighlightColor());
    		g.setFont(font);
    	
   	 	    g.drawString(s, 5, FONT_SIZE*(offset));	
		    offset++;
		}
		
		// allocates the proper size for the canvas
   	 	public void sizeToFit() {
	
			int dataSize = offset * FONT_SIZE;

    		// make sure buffer is large enough to hold the tape
    		while (shouldGrow(dataSize))
    			super.growVertical();
   	 	}
   	 	
   	 	public boolean shouldGrow(int i) {   	 	
   	 		return ( i > panel.getBufferHeight());
   	 	}
   	 }
}
