// file: SubDiGraph.java
//

// import java libraries
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
import java.io.*;
import java.lang.*;
import java.net.*;

/**
 * class that handles the actions of the Sub DiGraph frame
 */
public class SubDiGraph extends JFrame implements ActionListener, 
						  MouseListener,
						  MouseMotionListener, 
						  Constants {

    // ---------------------------------------------------
    //
    // declare class member data
    //
    // ---------------------------------------------------

    // keep track the curren vertex in focus
    //
    public Vertex initVertexFocus = null;
    public Vertex cfgVertexFocus = null;
    public Vertex currVertexFocus = null;

    // differentiate between parent and child 
    //
    public Vertex vertexParent = null;
    public Vertex vertexChild = null;

    // various types of been in the work area
    //
    public Vector<Vertex> vertices = null;
    public Vector<String> coeffLables = null;

    // flag that enables insertion and removal of arcs
    //
    public boolean enableArcInsertion = false;
    public boolean enableArcDeletion = false;

    // clipboard to handle things like cut, copy and paste
    //
    public Vector<Object> clipboard = null;

    // holds the maximum width and height of the scroller
    //
    public int maxWidth = 0;
    public int maxHeight = 0;

    // holds the current location of the mouse
    //
    public int mouseXloc = 0;
    public int mouseYloc = 0;

    // the label of the vertex that has been selected from the menu
    //
    public String currVertexLabel = null;

    // record the maximum number of inputs generated
    //
    public int maxInputVertices = 0;

    // algorithm association for the processes
    //
    public Association currAssociation = null;

    // maximum width and height of the work area
    //
    public int loadWidthMax = Integer.MIN_VALUE;
    public int loadHeightMax = Integer.MIN_VALUE;

    // creates a frame to manipulate the configuration options
    //
    public JFrame config = null;

    // textfields and labels for the connector properties
    //
    public JLabel toLabel = null;
    public JLabel fromLabel = null;
    public JLabel nameLabel = null;

    public JTextField toField = null;
    public JTextField fromField = null;
    public JTextField nameField = null;

    // main menu for functionality
    //
    public SubMainMenu subMainMenu = null;

    // work area to drag and drop components
    //
    public SubWorkArea subWorkArea = null;

    // paramenet file to be written to
    //
    public File paramFile = null;

    // scroll bars for the work area
    //
    public JScrollPane scroller = null;

    // declare the popup menu objects
    //
    public JPopupMenu popup = null;
    public JMenuItem menuItem = null;

    // default strings for the popup menus
    //
    public String ordering = null;
    public String delete = null;
    public String configuration = null;
    public String label = null;
    public String apply = null;
    public String update = null;
    public String cancel = null;
    public String saveApprove = null;
    public String loadApprove = null;

    // data object for reading the algorithm resource file
    //
    public Data data = null;
    public Parser parser = null;

    // create the start and term nodes
    //
    public Vertex START_VERTEX = new Vertex(new ImageIcon(ENTER_IMAGE), ALGORITHM);
    public Vertex TERM_VERTEX = new Vertex(new ImageIcon(EXIT_IMAGE), ALGORITHM);

    // ---------------------------------------------------
    //
    // declare class constructors
    //
    // ---------------------------------------------------
 
    /**
     * default class constructor
     *
     * @param title String title that is passed to the super() constructor
     * @param vec Vector of Vertices that is initialized to class vertex 
     *  variable
     */
    public SubDiGraph(String title, Vector<Vertex> vec) {
	
	// invoke the parent default constructor
	//
	super(title);

	//instantiate the data class
	//
	data = new Data();
	
	// run the parser
	//
	parser = new Parser(data);
	
	// initialize the vectors data members
	//
	vertices = vec;
	coeffLables = new Vector<String>(100, 50);
	clipboard = new Vector<Object>(10, 5);

	// instantiate the process algorithm association
	//
	currAssociation = new Association();

	// initialize the popup menu strings
	//
	ordering = new String("Ordering");
	delete = new String("Delete");
	configuration = new String("Configuration");

	// create the popup menu for the vertex
	//
	popup = new JPopupMenu();
	menuItem = new JMenuItem(configuration);
	menuItem.addActionListener(this);
	popup.add(menuItem);
	popup.addSeparator();

	menuItem = new JMenuItem(ordering);
	menuItem.addActionListener(this);
	popup.add(menuItem);

	menuItem = new JMenuItem(delete);
	menuItem.addActionListener(this);
	popup.add(menuItem);

	// set the font of the popup menu
	//
	popup.setFont(newCoeffFont);

	// create the menu to control functionality
	//
	subMainMenu = new SubMainMenu(this);

	// create a work area to drag and drop components
	//
	subWorkArea = new SubWorkArea(vertices, this);
	subWorkArea.addMouseListener(this);
	subWorkArea.addMouseMotionListener(this);

	// add scroll panes to the subWorkArea
	//
	scroller = new JScrollPane(subWorkArea, 
				   JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				   JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	
	// set the dimensions of the scroller
	//
	scroller.setPreferredSize(new Dimension(MAX_WIDTH, MAX_HEIGHT));

	// add the components
	//
	this.getContentPane().add(subMainMenu, BorderLayout.NORTH);
	this.getContentPane().add(scroller, BorderLayout.CENTER);
	subMainMenu.add_components();

	// add the vertices that were previously created
	//
	initVertices();
    }

    /**
     * initialize the start vertex
     */
    public void initStartVertex() {

	// set the location of the vertices
	//
	START_VERTEX.setLocation(new Point(INIT_START_XLOC, 
					   INIT_START_YLOC));

	// add mouse motion listeners to the vertices
	//
	START_VERTEX.addMouseListener(this);
	START_VERTEX.addMouseMotionListener(this);

	// set the dimensions of the vertices
	//
	START_VERTEX.setSize(ICON_WIDTH, ICON_HEIGHT);
	START_VERTEX.setPreferredSize(new Dimension(ICON_WIDTH, 
						    ICON_HEIGHT));
	START_VERTEX.setMinimumSize(new Dimension(ICON_WIDTH, 
						  ICON_HEIGHT));
	START_VERTEX.setVertexWidth(ICON_WIDTH);
	START_VERTEX.setVertexHeight(ICON_HEIGHT);

	// set the font of the vertices
	//
	START_VERTEX.setFont(newCoeffFont);

	// set the type of the vertex
	//
	START_VERTEX.setType(INPUT);

	// set the vertex papameters
	//
	START_VERTEX.association = new String(BUFFER_TAG);
	START_VERTEX.names = new Vector<Object>();
	START_VERTEX.types = new Vector<Object>();
    }
    
    /**
     * initialize the terminal vertex
     */
    public void initTermVertex() {
	
	// set the location of the vertices
	//
	TERM_VERTEX.setLocation(new Point(INIT_TERM_XLOC, 
					  INIT_TERM_YLOC));

	// add mouse motion listeners to the vertices
	//
	TERM_VERTEX.addMouseListener(this);
	TERM_VERTEX.addMouseMotionListener(this);     

	// set the dimensions of the vertices
	//
	TERM_VERTEX.setSize(ICON_WIDTH, ICON_HEIGHT);
	TERM_VERTEX.setPreferredSize(new Dimension(ICON_WIDTH, 
						   ICON_HEIGHT));
	TERM_VERTEX.setMinimumSize(new Dimension(ICON_WIDTH, 
						 ICON_HEIGHT));
	TERM_VERTEX.setVertexWidth(ICON_WIDTH);
	TERM_VERTEX.setVertexHeight(ICON_HEIGHT);

	// set the font of the vertices
	//
	TERM_VERTEX.setFont(newCoeffFont);

	// set the type of the vertex
	//
	TERM_VERTEX.setType(OUTPUT);

	// set the vertex papameters
	//
	TERM_VERTEX.association = new String(BUFFER_TAG);
	TERM_VERTEX.names = new Vector<Object>();
	TERM_VERTEX.types = new Vector<Object>();	
    }
    
    /**
     * resets the parent and child relationships
     */
    public void initVertices() {

	// check if the vector contains any elements
	//
	if (vertices.size() == 0) {

	    // initialize the start vertex
	    //
	    initStartVertex();

	    // initialize the term vertex
	    //
	    initTermVertex();

	    // add the vertices to the subWorkArea
	    //
	    subWorkArea.add(START_VERTEX);
	    vertices.add(START_VERTEX);
	    subWorkArea.add(TERM_VERTEX);
	    vertices.add(TERM_VERTEX);
	    return;
	}

	// add the elements to the subWorkArea
	//
	for (int i=0; i < vertices.size(); i++) {
	    
	    // get the current vertex
	    //
	    Vertex vertex = (Vertex)vertices.elementAt(i);
	    
	    // set the tool tip for the algorithm vertex
	    //
	    vertex.setToolTipText(vertex.association);
	    
	    // attach a mouse motion listener to the vertex
	    //
	    vertex.addMouseListener(this);
	    vertex.addMouseMotionListener(this);

	    // add the vertex to subWorkArea
	    //
	    subWorkArea.add(vertex);
	}
    }

    /**
     * hide the container window
     */
    public void closeContainer() {
	
	// remove the action listeners from the vertices
	//
	for (int i=0; i < vertices.size(); i++) {
	    Vertex vertex = (Vertex)vertices.elementAt(i);
	    vertex.removeMouseListener(this);
	    vertex.removeMouseMotionListener(this);
	}

	// hide the window
	//
	this.setVisible(false);
    }

    /**
     * resets the parent and child relationships
     */
    public void resetVertexFocus() {
	
	// reset the parent and child focus
	//
	vertexChild = null;
	vertexParent = null;

	// reset the current vertex selected
	//
	initVertexFocus = null;
	currVertexFocus = null;

	// reset the arc enables
	//
	enableArcInsertion = false;
	enableArcDeletion = false;

	// repaint the workarea
	//
	subWorkArea.repaint();
    }

    /**
     * updates the parent and child relationships
     * @param e input mouse event
     */
    public void updateVertexFocus(MouseEvent e) {
	
	// check to make sure that the vertex parent is valid
	//
	if (vertexParent == null) {
	    
	    // highligh the current vertex parent
	    //
	    vertexParent = (Vertex)e.getSource();
	}

	// initialize the child if the vertex parent is valid
	//
	else {

	    // initialize the child of the connection
	    //
	    vertexChild = (Vertex)e.getSource();
	}
    }

  
    /**
     * this method creates an algorithm object
     * @param algorithmstr name of the algorithm that fired the event
     */
    public void createAlgorithm(String algorithmstr) {

	// determine which algorithm fired the event
	//
	int action = data.association.indexOf(algorithmstr);
	
	// if the action is found in the list of transforms
	//
	if (action != -1) {
	    
	    String image_name = null;
	    String image_association= null;
	    String icon_name = null;

	    // tells the program which transform it is looking to match
	    //
	    image_association = algorithmstr;
	    
	    // scans through the association until 
	    // it finds the largeIcon key
	    //
	    for(int j=data.association.indexOf(image_association); 
		j <= data.association.lastIndexOf(image_association); 
		j++) {
		
		if (j != -1) {
		    if (((String)(data.name.get(j))).equals(ALGORITHM_ICON_BIG)) {
			
			// sets the image_name to the value held 
			// in large icon
			//
			image_name = (String)data.values.elementAt(j);
		    }
		    if (((String)(data.name.get(j))).equals("iconName")) {
			
			// sets the image_name to the value held 
			// in large icon
			//
			icon_name = (String)data.values.elementAt(j);
		    }  
		}
	    }
	    
	    // check to see if the file exists. If it does not exist,
	    // use a default image.
	    //
	    if(image_name != null) {
		
		File image_file = new File(image_name);
		if (image_file.exists() == false)
		    image_name = ALGORITHM_DEFAULT;
	    }
	    else {
		image_name = ALGORITHM_DEFAULT;
	    }
	    
	    // create the vertex for the work area
	    //
	    ImageIcon vertex_icon = new ImageIcon(image_name);
	    Vertex  vertex = new Vertex(vertex_icon , ALGORITHM);
	    vertex.setVertexName(algorithmstr);
	    if (algorithmstr.length() > 4) {
		vertex.setText(algorithmstr.substring(0, 4));
	    }
	    else {
		vertex.setText(algorithmstr);
	    }

	    if(algorithmstr.equals("Generator")) {
		vertex.setText("");
		vertex.setVertexImpl(INPUT);
	    }
	    if(algorithmstr.equals("FilterBank")) {
		vertex.setText("FilB");
	    }
	    
	    if(icon_name != null) {
		vertex.setText(icon_name);
		vertex.setIconName(icon_name);
	    }

	    // to simplify the creation, the data vectors will be
	    // trimmed to only include the transform we're looking
	    // at and saved into these three vectors for use in a
	    // bit
	    //
	    Vector<Object> labels = new Vector<Object>(5,5);
	    Vector<Object> types = new Vector<Object>(5,5);
	    Vector<Object> values = new Vector<Object>(5,5);
	    
	    for(int j=data.association.indexOf(image_association); 
		j <= data.association.lastIndexOf(image_association); 
		j++) {
		
		if(j != -1) {
		    labels.add(data.name.get(j));
		    types.add(data.type.get(j));
		    values.add(data.values.get(j));
		}
	    }	 
	    
	    // initialize the vertex to the default values for its type
	    //
	    vertex.initializeConfig(labels, values, types, 
				  image_association, data);

	    // set the tool tip for the algorithm vertex
	    //
	    vertex.setToolTipText(vertex.association);

	    // attach a mouse motion listener to the vertex
	    //
	    vertex.addMouseListener(this);
	    vertex.addMouseMotionListener(this);
	    
	    // set the current vertex selected
	    //
	    initVertexFocus = vertex;
	}
    }

    /**
     * method that removes the specified vertex from the work area as
     * well as removing all parent and corresponding children links
     *
     * @param target vertex to be removed
     */
    public void removeVertex(Vertex target) {

	// verify that the vertex to be deleted is valid
	//
	if (target == null) {
	    return;
	}

	// reset the vertex focus
	//
	resetVertexFocus();

	// get the parents of the traget and all accurances of target
	//
	Vector parents = target.getParents();
	for (int i=0; i<parents.size(); i++) {
	    if (((Vertex)(parents.elementAt(i))).containsChild(target)) {
		((Vertex)(parents.elementAt(i))).removeChild(target);
	    }
	}

	// get the children of the traget and all accurances of target
	//
	Vector children = target.getChildren();
	for (int i=0; i<children.size(); i++) {
	    if (((Vertex)(children).elementAt(i)).containsParent(target)) {
		((Vertex)(children.elementAt(i))).removeParent(target);
	    }
	}
	
	// remove the node form the work area
	//
	subWorkArea.remove(target);
	vertices.remove(target);

	// remove the label from the vector depending on the implementation
	//
	coeffLables.remove(target.getText());

	// repaint the work area
	//
	subWorkArea.repaint();
    }
  
    /**
     * method cuts the current vertex and adds it to the clipboard
     */
    public void cut() {

	// check for a valid vertex
	//
	if (currVertexFocus == null) {
	    return;
	}

	// clear the current contents of the clipboard
	//
	clipboard.removeAllElements();

	// add the vertex to the clipboard
	//
	clipboard.addElement(currVertexFocus);

	// remove the vertex 
	//
	removeVertex(currVertexFocus);

	// repaint the work area
	//
	subWorkArea.repaint();
    }

    /**
     * method copies the current vertex and adds it to the clipboard
     */
    public void copy() {

	// check for a valid vertex
	//
	if (currVertexFocus == null) {
	    return;
	}

	// clear the current contents of the clipboard
	//
	clipboard.removeAllElements();

	// add the vertex to the clipboard
	//
	clipboard.addElement(currVertexFocus);

	// repaint the work area
	//
	subWorkArea.repaint();
    }

    /**
     * method pastes the current vertex from the clipboard
     */
    public void paste() {

	// loop through each element in the clipboard and add them back
	//
	for(int i=0; i < clipboard.size(); i++) {

	    // create a copy of the vertex and remove all arcs
	    //
	    Vertex vertex = new Vertex((Vertex)clipboard.elementAt(i));
	    vertex.vertexParents.removeAllElements();
	    vertex.vertexChildren.removeAllElements();

	    // attach a mouse motion listener to the vertex
	    //
	    vertex.addMouseListener(this);
	    vertex.addMouseMotionListener(this);

	    // set the font for the vertex
	    //
	    vertex.setFont(newCoeffFont);

	    // set the location of the vertex
	    //
	    Point coordinates = new Point(mouseXloc, mouseYloc);
	    vertex.setLocation(coordinates);

	    // add the element to the data structures
	    //
	    vertices.addElement(vertex);
	    subWorkArea.add(vertex);
	}

	// repaint the work area
	//
	subWorkArea.repaint();
    }

    /**
     * method clears out all memory, i.e., vectors and hash tables
     */
    public void clearAllMemory() {

	// reset the maximum number of vertices
	//
	maxInputVertices = 0;
	
	// reset the vertices
	//
	initVertexFocus = null;
	cfgVertexFocus = null;
	currVertexFocus = null;
	vertexParent = null;
	vertexChild = null;

	// clear out all vectors
	//
	vertices.removeAllElements();
	coeffLables.removeAllElements();
	clipboard.removeAllElements();

	// clear out the work area
	//
	subWorkArea.removeAll();

	// add the start and term vertices back
	//
	initVertices();
    }
    
    // ---------------------------------------------------
    //
    // class required methods
    //
    // ---------------------------------------------------

    /**
     * Handles a mouse click event
     * @param e event that was fired
     */
    public void mouseClicked(MouseEvent e) {

	// declare local variables
	//
	boolean forwardLink = false;
	boolean reverseLink = false;

	Vertex conChild = null;
	Vertex conParent = null;

	// did the user click on the subWorkArea with the left mouse button?
	//
	if (e.getSource().getClass().getName().equals("SubWorkArea")) {

	    // reset the current focus
	    //
	    currVertexFocus = null;

	    if (SwingUtilities.isLeftMouseButton(e)) {

		// declare local variables
		//
		double smallestDistance = Double.MAX_VALUE;
		Vertex smallestParent = null;
		Vertex smallestChild = null;

		// has the arc deletion option been enabled?
		//
		if (enableArcDeletion) {		   
		    
		    // iterate through all vertices
		    //
		    for (int i=0; i<vertices.size(); i++) {
			
			// get the vertex which we will call the parent
			//
			Vertex delParent = (Vertex)vertices.elementAt(i);
			Vector delChildren = delParent.vertexChildren;

			// get the children associated with the parent
			//
			for (int j=0; j<delChildren.size(); j++) {
			    
			    // get a child of the current parent
			    //
			    Vertex delChild = (Vertex)delChildren.elementAt(j);
			    
			    // find the distance to the point of intersection 
			    // between the arc and the normal to the arc
			    //
			    Point ploc = delParent.getVertexLocation();
			    Point cloc = delChild.getVertexLocation();

			    // get the arc start coordinates
			    //
			    int x1 = ploc.x + (delParent.getVertexWidth() / 2);
			    int y1 = maxHeight - ploc.y + (delParent.getVertexHeight() / 2);

			    // get the arc stop coordinates
			    //
			    int x2 = cloc.x + (delChild.getVertexWidth() / 2);
			    int y2 = maxHeight - cloc.y + (delChild.getVertexHeight() / 2);

			    // get the slope of the line
			    //
			    double m1 = 0.000000001;
			    double dy = (double)(y2-y1);
			    double dx = (double)(x2-x1);
			    if (x2 != x1) {
				m1 = dy/dx;
			    }
			    double m11 = -1/m1;

			    // get the coordinates of the mouse click
			    //
			    int x = e.getX();
			    int y = maxHeight - e.getY();

			    // get the point of intersection
			    //
			    double x0 = ((-m11*x)+y+(m1*x1)-y1)/(m1-m11);
			    double y0 = (m11*x0)-(m11*x)+y;

			    // get the distance from the mouse click to the 
			    // point where the norlam intersects the line
			    //
			    double dist = Math.sqrt(Math.abs(((x-x0)*(x-x0))+((y-y0)*(y-y0))));
			    if (dist < smallestDistance) {
				smallestDistance = dist;
				smallestParent = delParent;
				smallestChild = delChild;
			    }			  
			}
		    }

		    // delete the arc that matches the constraints
		    //
		    if (smallestDistance > 0) {
			if ((smallestParent != null) && 
			    (smallestChild != null)) {
			    smallestParent.removeChild(smallestChild);
			    smallestChild.removeParent(smallestParent);
			}
		    }

		    // reset and repaint
		    //
		    resetVertexFocus();
		    return;
		}

		// is the newly created vertex valid?
		//
		if (initVertexFocus != null) {

		    // get the current cursor's location
		    //
		    Point location = e.getPoint();
		    
		    // retrieve the vertex dimensions
		    //
		    int vertexWidth = initVertexFocus.getVertexWidth();
		    int vertexHeight = initVertexFocus.getVertexHeight();
		    
		    // center the vertex around the mouse cursor
		    //
		    location.x = location.x - (vertexWidth / 2);
		    location.y = location.y - (vertexHeight / 2);
		    
		    // set the location of the vertex
		    //
		    initVertexFocus.setLocation(location);
		    
		    // add the vertex to the work area
		    //
		    subWorkArea.add(initVertexFocus);
		    vertices.add(initVertexFocus);

		    // reset the focus
		    //
		    resetVertexFocus();		    
		    return;
		}
	    }
	}

	// did the user click on a vertex with the right mouse button?
	//
	if (e.getSource().getClass().getName().equals("Vertex")) {
	    if(SwingUtilities.isRightMouseButton(e)) {

		// if the user clicked on a vertex reset the arc enables
		//
		enableArcInsertion = false;
		enableArcDeletion = false;

		// get the vertex that is being referenced
		//
		cfgVertexFocus = (Vertex)e.getSource();

		// display the popup menu based on the vertex type
		//
		popup.show(e.getComponent(), e.getX(), e.getY());

		// reset the focus
		//
		resetVertexFocus();
		return;
	    }
	}

	// did the user click on a vertex with the left mouse button?
	//
	if (e.getSource().getClass().getName().equals("Vertex")) {
	    if (SwingUtilities.isLeftMouseButton(e)) {	    

		// updates the vertex focus to highlight the current parent
		//
		updateVertexFocus(e);

		// exit the method if the vertex child is not instantiated
		//
		if (vertexChild == null) { 
		    return; 
		}

		// the input vertices cannot have incoming arcs
		//
		if (vertexChild.getVertexImpl() == INPUT) {
		    Toolkit.getDefaultToolkit().beep();
		    resetVertexFocus();
		    return;
		}
		
		// check for self loops and exit if it exists
		//
		if (vertexChild == vertexParent) {
		    Toolkit.getDefaultToolkit().beep();
		    resetVertexFocus();
		    return;
		}

		// make sure are drawing is enabled
		//
		if (!enableArcInsertion) {
		    resetVertexFocus();
		    return;
		}
		
		// check for forward and backward edges
		//
		if (vertexParent.containsChild(vertexChild)) {
		    forwardLink = true;
		}
		if (vertexChild.containsChild(vertexParent)) {
		    reverseLink = true;
		}
		
		// if neither a forward nor backward link exist add
		// the connector
		//
		if (!forwardLink && !reverseLink) {

		    // add the parent from the child lis and vice-versa
		    //
		    vertexParent.addChild(vertexChild);
		    vertexChild.addParent(vertexParent);
		}
		
		// reset the vertex parent and vertex child pointers
		//
		resetVertexFocus();
	    }
	}
	
	// update the work area
	//
	subWorkArea.repaint();
    }

    /**
     * Handles a mouse press event
     * @param e event that was fired
     */
    public void mousePressed(MouseEvent e) {

	// did the user click on a vertex with the left mouse button?
	//
	if (e.getSource().getClass().getName().equals("Vertex")) {
	    if (SwingUtilities.isLeftMouseButton(e)) {
		currVertexFocus = (Vertex)e.getSource();
	    }
	}

	// repaint the work area
	//
	subWorkArea.repaint();
    }

    /**
     * Handles a mouse drag event
     * @param e event that was fired
     */
    public void mouseDragged(MouseEvent e) {	

	// declare local variables
	//
	int xdelta = 0;
	int ydelta = 0;
	int newWidth = 0;
	int newHeight = 0;
	boolean forwardLink = false;
	boolean reverseLink = false;

	// did the user click on a vertex with the left mouse button?
	//
	if (e.getSource().getClass().getName().equals("Vertex")) {
	    if (SwingUtilities.isLeftMouseButton(e)) {

		// get the current location of the vertex
		//
		if (currVertexFocus != null) {

		    // determine the location and width of the vertex
		    //
		    Point loc = currVertexFocus.getVertexLocation();
		    
		    int vertexWidth = currVertexFocus.getVertexWidth();
		    int vertexHeight = currVertexFocus.getVertexHeight();
		    
		    int xloc = (loc.x + e.getX() - (vertexWidth / 2));
		    int yloc = (loc.y + e.getY() - (vertexHeight / 2));
		    
		    int xloc1 = loc.x + e.getX() + (vertexWidth / 2);
		    int yloc1 = loc.y + e.getY() + (vertexHeight / 2);

		    // make sure the user does not move the vertex out of
		    // the work area
		    //
		    if ((xloc >= 0) && (yloc >= 0)) {
			
			// determine the new width for the work area
			//
			if (xloc1 > subWorkArea.getWidth()) {

			    xdelta = xloc1 - subWorkArea.getWidth();
			    newWidth = subWorkArea.getWidth() + xdelta;
			}
			
			// determine the new height for the work area
			//
			if (yloc1 > subWorkArea.getHeight()) {
                            
			    ydelta = yloc1 - subWorkArea.getHeight();
			    newHeight = subWorkArea.getHeight() + ydelta;
			}

			// update the scroll bars of the work area
			//
			if ((xloc1 >= subWorkArea.getWidth()) || 
			    (yloc1 >= subWorkArea.getHeight())) {

			    // scroll the work area to the new region
			    //
			    Rectangle rect = new Rectangle(xloc, yloc, 
							   vertexWidth, 
							   vertexHeight);
			    subWorkArea.scrollRectToVisible(rect);
			    
			    // make sure that the width and does not
			    // decrease, i.e., it does not scroll back
			    //
			    if (newWidth <= maxWidth) {
				newWidth = maxWidth;
			    } else {
				maxWidth = newWidth;
			    }
			    
			    // make sure that the height and does not
			    // decrease, i.e., it does not scroll back
			    //
			    if (newHeight <= maxHeight) {
				newHeight = maxHeight;
			    } else {
				maxHeight = newHeight;
			    }
			    
			    // update client's preferred size because the area 
			    // taken up by the work area has gotten larger
			    //
			    subWorkArea.setPreferredSize(new Dimension(newWidth, 
								   newHeight));
			    
			    // let the scroll pane know to update itself
			    // and its corresponding scroll bars
			    //
			    subWorkArea.revalidate();
			}
			
			// drag the vertex to the location the user specifies
			//
			currVertexFocus.setLocation(xloc, yloc);
			currVertexFocus.updateLocation(xloc, yloc);

			// repaint the work area
			//
			subWorkArea.repaint();
		    }
		}

		// when the arc insertion is enabled
		//
		if (enableArcInsertion) {
		    
		    // updates the vertex focus to highlight the current parent
		    //
		    updateVertexFocus(e);
		    
		    // exit the method if the vertex child is not instantiated
		    //
		    if (vertexChild == null) { 
			return; 
		    }
		    
		    // the input vertices cannot have incoming arcs
		    //
		    if (vertexChild.getVertexImpl() == INPUT) {
			Toolkit.getDefaultToolkit().beep();
			resetVertexFocus();
			return;
		    }
		    
		    // the output vertices cannot have outgoing arcs
		    //
		    if(vertexParent.getVertexImpl() == OUTPUT) {
			Toolkit.getDefaultToolkit().beep();
			resetVertexFocus();
			return;
		    }
		    
		    // check for self loops and exit if it exists
		    //
		    if (vertexChild == vertexParent) {
			Toolkit.getDefaultToolkit().beep();
			resetVertexFocus();
			return;
		    }
		    
		    // check for forward and backward edges
		    //
		    if (vertexParent.containsChild(vertexChild)) {
			forwardLink = true;
		    }
		    if (vertexChild.containsChild(vertexParent)) {
			reverseLink = true;
		    }
		    
		    // if neither a forward nor backward link exist add
		    // the connector
		    //
		    if (!forwardLink && !reverseLink) {
			
			// add the parent from the child lis and vice-versa
			//
			vertexParent.addChild(vertexChild);
			vertexChild.addParent(vertexParent);
		    }
		    
		    // reset the vertex parent and vertex child pointers
		    //
		    resetVertexFocus();
		}
		
		// repaint the work area
		//
		subWorkArea.repaint();
	    }
        }
    }
 
   /**
     * Handles a mouse release event
     * @param e event that was fired
     */
    public void mouseReleased(MouseEvent e) {
	// not implemented
    }
    
    /**
     * Handles a mouse move event
     * @param e event that was fired
     */
    public void mouseMoved(MouseEvent e) {
	
	// update the location of the mouse coordinates
	//
	mouseXloc = e.getX();
	mouseYloc = e.getY();
    }
    
    /**
     * Handles a mouse exit event
     * @param e event that was fired
     */
    public void mouseExited(MouseEvent e) {
	// this method is required to be present
    }
    
    /**
     * Handles a mouse enter event
     * @param e event that was fired
     */
    public void mouseEntered(MouseEvent e) {
	// this method is required to be present
    }

  
    /**
     * listens for actions taking place on the components
     *
     * @param e event that was fired
     */
    public void actionPerformed(ActionEvent e) {

	System.out.println("SubDigraph action");
	// detremine if the delete option was selected
	//
	if (e.getActionCommand().equals(delete)) {
	    removeVertex(cfgVertexFocus);
	}

	// determine if the configuration option was selected
	//
	else if (e.getActionCommand().equals(ordering)) {

	    // create a new configuration box and display in a new frame
	    //
	    if (cfgVertexFocus != null) {
		ConfigBoxSub config_box = new ConfigBoxSub(this, data, cfgVertexFocus, subWorkArea, 1);
		config_box.pack();
		config_box.setVisible(true);
	    }
	}

	// determine if the configuration option was selected
	//
	else if (e.getActionCommand().equals(configuration)) {
	    
	    // create a new configuration box and display in a new frame
	    //
	    if (cfgVertexFocus != null) {
		ConfigBoxSub config_box = new ConfigBoxSub(this, data, cfgVertexFocus, subWorkArea, 0);
		config_box.pack();
		config_box.setVisible(true);
	    }
	}

	// detremine if the update was selected
	//
	else if (e.getActionCommand().equals(update)) {

	    // check to see if a vertex with this label exists
	    //
	    if (coeffLables.contains(nameField.getText())) {

		// display a warning message indicating that you cannot have
		// two vertices with the same name
		//
		JFrame frame = new JFrame("Warning");
		JOptionPane.showMessageDialog(frame, "A node with the label " 
					      + nameField.getText() + 
					      " already exists");
		return;
	    }

	    // remove the old and add the new label
	    //
	    coeffLables.remove(cfgVertexFocus.getText());
	    coeffLables.addElement(nameField.getText());

	    // update the vertex text with what the user entered
	    //
	    if (nameField.getText().length() > 3) {
		cfgVertexFocus.setText(nameField.getText().substring(0, 3));
	    }
	    else {
		cfgVertexFocus.setText(nameField.getText());
	    }
	    cfgVertexFocus.setVertexName(nameField.getText());

	    // remove the frame from the screen
	    //
	    config.setVisible(false);
	}

	// detremine if the cancel was selected
	//
	else if (e.getActionCommand().equals(cancel)) {

	    // remove the frame from the screen
	    //
	    config.setVisible(false);
	}

	// repaint the work area
	//
	subWorkArea.repaint();
    }
}

//
// end of file





