/*
 * Arrow.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 edu.neu.ccs.util.*;
import java.awt.*;
import java.awt.geom.*;
import java.text.ParseException;
import java.awt.font.*;

// controls the graphical representation of 
// arrows and transitions in the automata window
public class Arrow {
	
	// Constant: default color
	protected static final Color DEFAULT_COLOR = Color.black;
	
	// Constant: default font 
	private static final int FONT_SIZE = 16;
	private Font font = new Font("SansSerif", Font.BOLD, FONT_SIZE);
	
	private State source = null;
	private State target = null;
	private XVector transitions = null;
	
	// arrow can be a line 
	// or an ellipse
	private Shape arrowShape;

	// constructor
	public Arrow() {}
	
	// set the arrow's data
	public void set(State s, State t, XVector v){
		
		source = s;
		target = t;
		transitions = v;
		arrowShape = getArrowShape();
	}

	// draw the arrow connecting
	// source and target in the given color
	public void draw(Graphics2D g, Color c) {

		// don't draw if no data
		if (source == null || target == null)
			return;
			
		// set drawing color
		if (c == null)
			g.setColor(DEFAULT_COLOR);
		else
			g.setColor(c);
	
		// draw the arrow	
		g.draw(arrowShape);
		
		// arrow will overwrite states,
		// source and target must be redrawn
		source.draw(g, c);
		target.draw(g, c);
	}
	
	// Draws all transitions
	public void drawTransitions(Graphics2D g, Color c) {

		// return if no data
		if (transitions == null) 
			return;
		
		String label = "";
		Point  location = null;
		int size = transitions.size();
				
		// set drawing color
		if (c == null)
			g.setColor(DEFAULT_COLOR);
		else
			g.setColor(c);
			
		// set drawing font
		g.setFont(font);
		
		// draw all transitions
		for(int i = 0; i < size; i++) {
						
			label = ((Transition) transitions.get(i)).toStringData() +
			    " ->" + target.getName();
			
			location = locate(g, i, label);
			g.drawString(label, (int)location.getX(), 
							    (int)location.getY());	
		}
			
	}
	
	// draws a specific transition according to the selector
	public void drawTransition(Graphics2D g, Color c, 
							   int selector, Transition t) {

		String label = "";
		Point  location = null;
				
		// set drawing color
		if (c == null)
			g.setColor(DEFAULT_COLOR);
		else
			g.setColor(c);
			
		// set drawing font
		g.setFont(font);
		
		// draw single transition
		label = t.toStringData() + " ->" + target.getName();
		location = locate(g, selector, label);
		g.drawString(label, (int)location.getX(), 
							(int)location.getY());
			
	}
	
	public Shape getArrowShape() {
		
		Point sourceCenter = source.getCenter();
		Point targetCenter = target.getCenter();
		
		int radius = source.getRadius();
		
		// if the origin and destination are the same, 
		// the arrow shape should be a loop
		if ((sourceCenter.getX() == targetCenter.getX()) &&
		    (sourceCenter.getY() == targetCenter.getY())) {
		
			return new Ellipse2D.Double(sourceCenter.getX(), 
										sourceCenter.getY(),
										radius*.75, 
										radius*.75);
		}
			
		// otherwise create a line from the center
		// of the origin to the center of the destination
		return new Line2D.Double(sourceCenter, targetCenter);	
	}
	
	public Point locate(Graphics2D g, int selector, String label) {
	
		int offset = getLabelOffset() * (selector + 1);
		
	    FontRenderContext frc =
            g.getFontRenderContext();
            
        Rectangle2D stringBounds =
            g.getFont().getStringBounds(label, frc);
	
		
		Rectangle2D boundingBox = arrowShape.getBounds2D();
		
		if (arrowShape instanceof Ellipse2D.Double) 
		
			return new Point((int) boundingBox.getMaxX()+2, 
						 	 (int) boundingBox.getCenterY()
						 	 + offset);	
		else
			return new Point( 
			    ((int) boundingBox.getCenterX()) - ((int) stringBounds.getWidth() / 2) ,
			    (int) boundingBox.getCenterY() - offset);
	}
	
	public int getLabelOffset() {
		
		Point sourceCenter = source.getCenter();
		Point targetCenter = target.getCenter();
		
		if(sourceCenter.getX() > targetCenter.getX())
			return FONT_SIZE;
		
		if(sourceCenter.getX() < targetCenter.getX())
			return (-1 * FONT_SIZE);
		
		if(sourceCenter.getY() > targetCenter.getY())
			return FONT_SIZE;
			
		if(sourceCenter.getY() < targetCenter.getY())
			return (-1 * FONT_SIZE);
			
		return FONT_SIZE; 
	}	
	
	public void animate(BufferedPanel p, int selector, 
						Transition t, int speed){
					 	
		Graphics2D g = p.getBufferGraphics();
		
		Color highlight = 
			AutomataApplication.getHighlightColor();
			
		Color visited = 
			AutomataApplication.getVisitedColor();

		drawTransition(g, highlight, selector, t);
		p.repaint();
		
		JPTUtilities.pauseThread(speed);
		
		draw(g, visited);
		drawTransition(g, visited, selector, t);
		p.repaint();
		
		JPTUtilities.pauseThread(speed);
					 		
	}
}

