// file: WorkArea.java
//

// import java packages
//
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 working area of the program
 */
public class WorkArea extends JPanel {

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

    // declare a vector of all elements in the graph
    //
    public int workAreaWidth = CData.WORKAREA_WIDTH;
    public int workAreaHeight = CData.WORKAREA_HEIGHT;

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

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

    /**
     * inserts all the current vertices to the panel
     */
    public void insertAll() {

	if ( digraph_d == null ){
	    return;
	}
	
	// remove all components
	//
	removeAll();
	
	// add vertices
	//
	for (int i = 0 ; i < digraph_d.vertices_d.size(); i ++ ){
	    Vertex curr_vertex = (Vertex) digraph_d.vertices_d.get(i);
	    //insertVertexAt(curr_vertex, curr_vertex.getVertexLocation());
	    insertVertexAt(curr_vertex, curr_vertex.getLocation());
	    //add(curr_vertex);
	}	
	
    }

    /**
     * default class constructor, calls super() constructor
     *
     */ 
    public WorkArea() {

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

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

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

    /**
     * add the vertex to the work area, calls vector add() method
     * 
     * @param vertex_a vertex to add 
     * @param point_a  point location of vertex
     */
    public void insertVertex(Vertex vertex_a, Point point_a) {

	Point location = new Point();
	 
	// retrieve the vertex dimensions
	//
	int vertexWidth = vertex_a.getVertexWidth();
	int vertexHeight = vertex_a.getVertexHeight();
	
	// center the vertex around the mouse cursor
	//
	location.x = point_a.x - (vertexWidth / 2);
	location.y = point_a.y - (vertexHeight / 2);
	vertex_a.setLocation(location);
		    
	add(vertex_a);	
    }

    /**
     * add the vertex to the work area
     * 
     * @param vertex_a vertex to add
     * @param point_a  point location of vertex
     *
     */
    public void insertVertexAt(Vertex vertex_a, Point point_a) {

	vertex_a.setLocation(point_a);
	add(vertex_a);	
	CData.printDebugMsg("add vertex: " + vertex_a.name_d);
	Dimension size = getPreferredSize();
	int height = (int)Math.max(point_a.y + 180 , size.getHeight() );
	int width = (int)Math.max(point_a.x + 180, size.getWidth() );
	setPreferredSize(new Dimension(height, width));
    }

    /**
     * add the vertex to the work area
     * 
     * @param vertex_a vertex to be added
     */
    public void insertVertex(Vertex vertex_a) {
		    
	add(vertex_a);	
    }

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

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

	// return the width
	//
	return workAreaWidth;
    }

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

	// return the height
	//
	return workAreaHeight;
    }

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

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

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

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

    /**
     * sets the DiGraph object of the class
     *
     * @param digraph_a DiGraph object to be set
     */
    public void setDiGraph(DiGraph digraph_a) {

	// set the height
	//
	digraph_d = digraph_a;
    }

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

    /**
     * paint the current data points and if needed the decision regions
     *
     * @param g_a Graphics object to use
     */   
    public void paintComponent(Graphics g_a) {

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

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

	boolean is_self_arc = false;
	boolean is_arc = false;

	// check the menu again
	//
	CData.main_frame_d.checkMenu();

	// invoke the super constructor
	//
        super.paintComponent(g_a);
	
	/*
	// set all vertices to be invisible
	//
	for ( int i = 0; i < CData.search_levels_d.size(); i++ ){
	    SearchLevel curr_level = 
		(SearchLevel) CData.search_levels_d.get(i);
	    for ( int j = 0 ; j < curr_level.graph_names_d.size(); j++ ){
		DiGraph curr_graph = (DiGraph)
		    curr_level.graphs_d.get(curr_level.graph_names_d.get(j));
		curr_graph.hideVertices();
	    }
	}
	*/

	Graphics2D g = (Graphics2D)g_a;
	CData.printDebugMsg("1 repaint work area... " );

	// do nothing if current digraph is null
	//
	if (digraph_d == null )
	    return;

	// insert all vertices
	//
	//insertAll();
	CData.printDebugMsg("2 repaint work area... " );

	// show all the vertices
	//
	digraph_d.showVertices();

	// highlight the current vertex focus
	//

	if (CData.event_handler_d.curr_vertex_focus_d != null) {
	    //g.clearRect(0, 0, CData.WORKAREA_WIDTH, CData.WORKAREA_HEIGHT);
	    g.setColor(Color.blue);
	    Rectangle bounds = 
		CData.event_handler_d.curr_vertex_focus_d.getBounds();
	    g.drawRect(bounds.x - CData.HIGHLIGHT_WIDTH, 
		       bounds.y - CData.HIGHLIGHT_WIDTH, 
		       bounds.width + CData.HIGHLIGHT_WIDTH, 
		       bounds.height + CData.HIGHLIGHT_WIDTH);
	    g.setColor(Color.black);

	}

	// iterate through all arcs in the graph
	//
	for (int p=0; p < digraph_d.arcs_d.size(); p++) {
	    
	    is_self_arc = false;
	    int indexs = 0; 
	    GraphArc curr_arc =  (GraphArc)digraph_d.arcs_d.get(p);
	    Vertex from_vertex = curr_arc.from_d;
	    Vertex to_vertex = curr_arc.to_d;
	    Point loc1 = from_vertex.getLocation();
	    Point loc2 = to_vertex.getLocation();
	    
	    // determine the co-ordinates of the from_vertex
	    //
	    x1 = loc1.x + (from_vertex.getVertexWidth() / 2);
	    y1 = loc1.y + (from_vertex.getVertexHeight() / 2);
	      
	    String wei = curr_arc.weights_d;
	    boolean epi = curr_arc.epsilon_d;

	    if ( curr_arc == CData.event_handler_d.curr_arc_focus_d){
		g.setColor(Color.blue);
	    }
	    else{
		g.setColor(Color.black);
	    }

	    // if this arc is a self arc
	    //
	    if(curr_arc.isSelfArc() ) {
		g.drawArc(loc1.x + 12, 
			  loc1.y - from_vertex.getVertexWidth()/2 + 12, 
			  from_vertex.getVertexWidth() - 24, 
			  from_vertex.getVertexWidth() - 24, 
			  0, 180);

	    
		g.drawArc(loc1.x + 13, 
			  loc1.y - from_vertex.getVertexWidth()/2 + 13, 
			  from_vertex.getVertexWidth() - 26, 
			  from_vertex.getVertexWidth() - 26, 
			  0, 180);
	    
		int xtip1 = loc1.x + 12;
		int ytip1 = loc1.y + 12 - 4;
		int xleft1 = loc1.x + 12 - 7/2;
		int yleft1 = loc1.y  - 1;
		int xright1 = loc1.x + 12 + 7/2;
		int yright1 = loc1.y -1 ;
		
		int x0Pts[] = {xtip1, xright1, xleft1, xtip1};
		int y0Pts[] = {ytip1, yright1, yleft1, ytip1};
		
		// g.setColor(Color.red);
		g.fillPolygon(x0Pts, y0Pts, x0Pts.length);
	    
		// set x and y location
		//
		curr_arc.xloc_d = loc1.x + 12;
		curr_arc.yloc_d = loc1.y - 
		    from_vertex.getVertexWidth()/2 + 12 ;
		
		// set the width and height
		//
		curr_arc.width_d = from_vertex.getVertexWidth() - 24;
		curr_arc.height_d = (from_vertex.getVertexWidth() - 24)/2;

		g.setFont(CData.ARC_FONT);
		
		// if it is not a epsilon transition
		//
		if( ! epi ) {
		    g.drawString(""+ wei, 
				 loc1.x + from_vertex.getVertexWidth()/2,
				 loc1.y - from_vertex.getVertexWidth()/2 + 11);
		}
		else {
		    g.drawString(""+ "epi", 
				 loc1.x + from_vertex.getVertexWidth()/2,
				 loc1.y - from_vertex.getVertexWidth()/2 + 11);
		}
		g.setFont(CData.ARIAL_FONT);
	    }
	    // this arc is not a self loop
	    //
	    else{

		// determine the up-down co-ordinates of the to_vertex 
		//
		double delta = (double)to_vertex.getVertexWidth() / 
		    (double)(to_vertex.getInDegree() + 1);
		int index = to_vertex.parents_d.indexOf(from_vertex) + 2;
		x2 = loc2.x + (int)(delta * index);
		y2 = loc2.y + (to_vertex.getVertexHeight() / 2);
	      
		// determine the four destination points on the vertex 
		//
		int xpnt1 = x2;
		int ypnt1 = y2 - (to_vertex.getVertexHeight() / 2) 
		    - CData.VERTEX_MARGIN;
		int xpnt2 = x2;
		int ypnt2 = y2 + (to_vertex.getVertexHeight() / 2) 
		    + CData.VERTEX_MARGIN;
	      
		// determine the left-right co-ordinates of the to_vertex
		//
		double delta1 = (double)to_vertex.getVertexHeight() / 
		    (double)(to_vertex.getInDegree() + 1);
		int index2 = to_vertex.parents_d.indexOf(from_vertex) + 2;
		x21 = loc2.x + (to_vertex.getVertexWidth() / 2);
		y21 = loc2.y + (int)(delta1 * index2);
	      
		// determine the four destination points on the vertex 
		//
		int xpnt3 = x21 - (to_vertex.getVertexWidth() / 2) 
		    - CData.VERTEX_MARGIN;
		int ypnt3 = y21;
		int xpnt4 = x21 + (to_vertex.getVertexWidth() / 2) 
		    + CData.VERTEX_MARGIN;
		int ypnt4 = y21;
		
		// determine the closest destination to the from_vertex
		//
		int destVal = -1;
		double currMax = 0.0;
		double maxDist = Double.MAX_VALUE;
		
		// determine the distance to the first point
		//
		currMax = Math.abs(CData.distance(x1, y1, xpnt1, ypnt1));
		if (currMax < maxDist) {
		    destVal = 1;
		    maxDist = currMax;			
		}
		
		// determine the distance to the second point
		//
		currMax = Math.abs(CData.distance(x1, y1, xpnt2, ypnt2));
		if (currMax < maxDist) {
		    destVal = 2;
		    maxDist = currMax;			
		}
		
		// determine the distance to the third point
		//
		currMax = Math.abs(CData.distance(x1, y1, xpnt3, ypnt3));
		if (currMax < maxDist) {
		    destVal = 3;
		    maxDist = currMax;			
		}
		
		// determine the distance to the fourth point
		//
		currMax = Math.abs(CData.distance(x1, y1, xpnt4, ypnt4));
		if (currMax < maxDist) {
		    destVal = 4;
		    maxDist = currMax;			
		}

		// draw a directed arc form the from_vertex to the to_vertex
		//
		if (destVal == 1) {
		    x2 = xpnt1;
		    y2 = ypnt1;
		} 
		else if(destVal == 2){
		    x2 = xpnt2;
		    y2 = ypnt2;
		}
		else if(destVal == 3){
		    x2 = xpnt3;
		    y2 = ypnt3;
		}
		else {
		    x2 = xpnt4;
		    y2 = ypnt4;
		}

		// draw a line connecting the from_vertex and to_vertex
		//
		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 from_vertex to to_vertex
		//
		int dy = y2 - y1;
		int dx = x2 - x1;
		int ARROW_SIZE = 6;
		
		double dist = CData.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); 
	   
		// set x and y location
		//
		curr_arc.xloc_d = x1;
		curr_arc.yloc_d = y1;
		
		// set the width and height
		//
		curr_arc.width_d = x2;
		curr_arc.height_d = y2;

		g.setFont(CData.ARC_FONT);
		
		// draw the weights
		//
		if( ! epi ) {
		    g.drawString(""+ wei, (x1 + x2) /2 - 4, (y1 + y2) /2 - 
				 3 );
		}
		else {
		    g.drawString(""+ "epi", (x1 + x2) /2 - 4, (y1 + y2) /2 - 
				 3 );
		}
		g.setFont(CData.ARIAL_FONT);
	    } // end of else if this is not a self loop
	} // end of for loop for all arcs

	//setPreferredSize(new Dimension(6000, 10000));	
    } 
}
//
// end of file
