// file: WorkArea.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.awt.geom.*;

/**
 * class that handles the work area JPanel
 */
public class WorkArea extends JPanel implements Constants {

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

    // declare a vector of all elements in the graph
    //
    public Vector vertices = null;
    public int workAreaWidth = WORKAREA_WIDTH;
    public int workAreaHeight = WORKAREA_HEIGHT;

    // declare an instance of the directed graph
    //
    DiGraph digraph = null;

    // ---------------------------------------------------
    //
    // declare class constructors
    //
    // ---------------------------------------------------

    /**
     * default class constructor
     * 
     * @param vertices collection of vertices
     * @param digraph  DiGraph object to be used
     */
    public WorkArea(Vector vertices, DiGraph digraph) {

	// invoke the super constructor
	//
	super();

	// initialize
	//
	this.vertices = vertices;

	// initialize the digraph
	//
	this.digraph = digraph;

	// invalidate the default layout
	//
	this.setLayout(null);

	// set the default background color of the panel
	//
	this.setBackground(Color.white);
    }

    // ---------------------------------------------------
    //
    // declare class methods
    //
    // ---------------------------------------------------

    /**
     *  retrieves the width of the work area
     *
     * @return work area width
     */
    public int getAreaWidth() {

	// return the width
	//
	return workAreaWidth;
    }

    /**
     * retrieves the height of the work area
     *
     * @return work area height
     */
    public int getAreaHeight() {

	// return the height
	//
	return workAreaHeight;
    }

    /**
     * sets the width of the work area
     * 
     * @param width width of the work area
     */
    public void setWidth(int width) {

	// set the width
	//
	workAreaWidth = width;
    }

   /**
     * sets the height of the work area
     * 
     * @param height height of the work area
     */
    public void setHeight(int height) {

	// set the height
	//
	workAreaHeight = height;
    }

    /**
     * calculate the euclidean distance between the two points
     *
     * @param x1 x-coordinate of the first point
     * @param y1 y-coordinate of the first point
     * @param x2 x-coordinate of the second point
     * @param y2 y-coordinate of the second point
     * @return distance between the points
     */
    public double distance(int x1, int y1, int x2, int y2) {

        double distance = 0.0;

        // claculate the euclidean distance
        //
        double deltaX = x2 - x1;
        double deltaY = y2 - y1;
        double sqrX = deltaX * deltaX;
        double sqrY = deltaY * deltaY;

        distance = Math.sqrt((double)sqrX + (double)sqrY);

        // return the distance
        //
        return distance;
    }

    // ---------------------------------------------------
    //
    // class required methods
    //
    // ---------------------------------------------------

    /**
     * paint the current data points and if needed the decision regions
     *
     * @param g graphics object to be used
     */
    public void paintComponent(Graphics g) {

	// declare local variables
	//
	int x1 = 0;
	int x2 = 0;
	int y1 = 0;
	int y2 = 0;

	Vertex parent = null;
	Vertex child = null;
	Vector children = null;

	// invoke the super constructor
	//
        super.paintComponent(g);

	// highlight the current vertex focus
	//
	if (digraph.currVertexFocus != null) {
	    g.setColor(Color.red);
	    Rectangle bounds = digraph.currVertexFocus.getBounds();
	    g.drawRect(bounds.x - HIGHLIGHT_WIDTH, 
		       bounds.y - HIGHLIGHT_WIDTH, 
		       bounds.width + HIGHLIGHT_WIDTH, 
		       bounds.height + HIGHLIGHT_WIDTH);
	    g.setColor(Color.black);
	}

	// iterate through all parents in the graph
	//
	for (int p=0; p < vertices.size(); p++) {
	    
	    parent = (Vertex)vertices.elementAt(p);
	    children = parent.getChildren();

	    // iterate through all children of the parent
	    //
	    for (int c=0; c < children.size(); c++) {
	    
		child = (Vertex)children.elementAt(c);
		
		// get the location of the parent and child
		//
		Point loc1 = parent.getVertexLocation();
		Point loc2 = child.getVertexLocation();
		
		// determine the co-ordinates of the parent
		//
		x1 = loc1.x + (parent.getVertexWidth() / 2);
		y1 = loc1.y + (parent.getVertexHeight() / 2);
		
		// determine the co-ordinates of the child
		//
		double delta = (double)child.getVertexWidth() / 
		    (double)(child.getInDegree() + 1);
		int index = child.vertexParents.indexOf(parent) + 1;
		x2 = loc2.x + (int)(delta * index);
		y2 = loc2.y + (child.getVertexHeight() / 2);
		
		// determine the four destination points on the vertex 
		//
		int xpnt1 = x2;
		int ypnt1 = y2 - (child.getVertexHeight() / 2) - VERTEX_MARGIN;
		int xpnt2 = x2;
		int ypnt2 = y2 + (child.getVertexHeight() / 2) + VERTEX_MARGIN;

		// determine the closest destination to the parent
		//
		int destVal = -1;
		double currMax = 0.0;
		double maxDist = Double.MAX_VALUE;
		
		// determine the distance to the first point
		//
		currMax = Math.abs(distance(x1, y1, xpnt1, ypnt1));
		if (currMax < maxDist) {
		    destVal = 1;
		    maxDist = currMax;			
		}
		
		// determine the distance to the second point
		//
		currMax = Math.abs(distance(x1, y1, xpnt2, ypnt2));
		if (currMax < maxDist) {
		    destVal = 2;
		    maxDist = currMax;			
		}
		
		// draw a directed arc form the parent to the child
		//
		if (destVal == 1) {
		    x2 = xpnt1;
		    y2 = ypnt1;
		} 
		else {
		    x2 = xpnt2;
		    y2 = ypnt2;
		}
		
		// draw a line connecting the parent and child
		//
		g.drawLine(x1, y1-1, x2, y2-1);
		g.drawLine(x1-1, y1, x2-1, y2);
		g.drawLine(x1, y1, x2, y2);

		// draw a directed arrow head from parent to child
		//
		int dy = y2 - y1;
		int dx = x2 - x1;
		int ARROW_SIZE = 6;
		
		double dist = distance(x1, y1, x2, y2);
		dist = dist == 0 ? 1 : dist;
		
		double medianx = x2 - dx * ARROW_SIZE / dist;
		double mediany = y2 - dy * ARROW_SIZE / dist;
		    
		int base1x = (int)(medianx - dy * 
				       ( ARROW_SIZE / 2 ) / dist);
		int base1y = (int)(mediany + dx * 
				   ( ARROW_SIZE / 2 ) / dist);
			
		int base2x = (int)(medianx + dy * 
				   ( ARROW_SIZE / 2 ) / dist);
		int base2y = (int)(mediany - dx * 
				       ( ARROW_SIZE / 2 ) / dist);
		
		int xPts[] = {x2, base1x, base2x, x2};
		int yPts[] = {y2, base1y, base2y, y2};
		g.fillPolygon(xPts, yPts, xPts.length); 
	    }
	}
    }
}

//
// end of file
