package drawing;

import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.Vector;

import drawing.Diagram;
import drawing.Link;
import drawing.UnitClass;

/**
 * This class represents a graph
 * @author MC
 *
 */
public abstract class Graph {
	/**
	 * contains the graph's vertices
	 */
	public Vector<Vertice> Vertices;
	/**
	 * contains the graph's edges
	 */
	public Vector<Edge> Edges;
	/**
	 * the minimal distance between two vertices on the x axis 
	 */
	final static int dxMin = 5;
	/**
	 * the minimal distance between two vertices on the y axis
	 */
	final static int dyMin = 50;
	
	/**
	 * constructor
	 * @param V @see Graph#Vertices
	 * @param E @see Graph#Edges
	 */
	public Graph(Vector<Vertice> V, Vector<Edge> E){
		Vertices = V;
		Edges = E;	
	}
	/**
	 * constructor
	 * @param dia uml diagram @see Diagram
	 */
	public Graph(Diagram dia){
		Vertices = new Vector<Vertice>();
		Edges = new Vector<Edge>();
		for(int i = 0; i < dia.vectClass.size(); i++){
			UnitClass u = (UnitClass) dia.vectClass.get(i);
			add(new Vertice(u,i));
		}
		for(int i = 0; i < dia.vectLink.size(); i++){
			Link l = (Link) dia.vectLink.get(i);
			int start, end;
			start = dia.vectClass.indexOf(l.startUnit);
			end = dia.vectClass.indexOf(l.endUnit);
			if(end != -1 && start != -1 && !l.autoRefers)
				add(new Edge((Vertice)Vertices.get(start), (Vertice)Vertices.get(end), true));
		}
	}
	/**
	 * constrctor
	 * @param dia the uml diagram
	 * @param classUnit the classes vector
	 * @param link the links vector
	 */
	public Graph(Diagram dia, Vector<UnitClass> classUnit, Vector<Link> link){
		Vertices = new Vector<Vertice>();
		Edges = new Vector<Edge>();
		for(int i = 0; i < classUnit.size(); i++){
			UnitClass u = (UnitClass) classUnit.get(i);
			add(new Vertice(u,i));
		}
		for(int i = 0; i < link.size(); i++){
			Link l = (Link) link.get(i);
			int start, end;
			start = classUnit.indexOf(l.startUnit);
			end = classUnit.indexOf(l.endUnit);
			if(end != -1 && start != -1 && !l.autoRefers)
				add(new Edge((Vertice)Vertices.get(start), (Vertice)Vertices.get(end), true));
		}
	}
	/**
	 * adds a vertice to the graph
	 * @param v the vertice to add
	 */
	public void add(Vertice v){
		Vertices.add(v);
	}
	/**
	 * adds a link to the graph
	 * @param l the link to add
	 */
	public void add(Edge l){
		if(Vertices.contains(l.start) && Vertices.contains(l.end))
			Edges.add(l);
	}
	/**
	 * computes the coordinates of the vertices
	 * @return the coordinates
	 */
	public abstract double[][] ComputeCoordinates();
	
	/**
	 * assigns the computed coordinates to the vertices
	 *
	 */
	public void AssignCoordinates(){
		double [][] XY = ComputeCoordinates();
		for(int i = 0; i < Vertices.size(); i++){
			Vertice v = (Vertice) Vertices.get(i);
			if(!v.isVirtual){
				v.y = XY[v.index][1];
				v.x = XY[v.index][0];
			}
		}
	}
	/**
	 * computes the crossings number among the graph
	 * @return the crossings number
	 */
	public int nbCrossing(){
		int cross = 0;
		for(int i = 0; i < Edges.size(); i++ ){
			Edge ei = (Edge) Edges.get(i);
			Line2D.Double line1 = new Line2D.Double(ei.end.x + (double)ei.end.width/2,
					ei.end.y + (double)ei.end.height/2, ei.start.x + (double)ei.start.width/2,
					ei.start.y + (double)ei.start.height/2);
			for(int j = 0; j < Edges.size(); j++ ){
				Edge ej = (Edge) Edges.get(j);
				if(i != j && ei.start.index != ej.start.index && ei.start.index != ej.end.index
						&& ei.end.index != ej.start.index && ei.end.index != ej.end.index){					
					Line2D.Double line2 = new Line2D.Double(ej.start.x + (double)ej.start.width/2,
							ej.start.y + (double)ej.start.height/2, ej.end.x + (double)ej.end.width/2,
							ej.end.y + (double)ej.end.height/2);
					if(line1.intersectsLine(line2)){
						cross++;
						//System.out.println(ei.end.index+","+ej.start.index+"//"+i+","+j);
					}
				}
			}
		}
		return cross/2;
	}
	/**
	 * computes the edges length
	 * @return
	 */
	public double edgesLength(){
		double l = 0;
		for(int i = 0; i < Edges.size(); i++ ){
			Edge ei = (Edge) Edges.get(i);
			double x1, y1, x2, y2;
			x1 = ei.start.x + (double)ei.start.width/2;
			y1 = ei.start.y + (double)ei.start.height/2;
			x2 = ei.end.x + (double)ei.end.width/2;
			y2 = ei.end.x + (double)ei.end.height/2;
			l += Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
		}
		return l;
	}
	/**
	 * computes the superposition ares of the vertices 
	 * @return
	 */
	public double superpositionAir(){
		double air = 0;
		for(int i = 0; i < Vertices.size(); i++){
			Vertice vi = (Vertice)Vertices.get(i);
			Rectangle2D.Double reci = new Rectangle2D.Double(vi.x, vi.y, vi.width, vi.height);
			for(int j = 0; j < Vertices.size(); j++){
				if(i != j){
					Vertice vj = (Vertice)Vertices.get(j);
					Rectangle2D.Double recj = new Rectangle2D.Double(vj.x, vj.y, vj.width, vj.height);
					Rectangle2D.Double rec = (Rectangle2D.Double)reci.createIntersection(recj);
					if(rec.height > 0 && rec.width>0)
						air += rec.height * rec.width;
				}
			}
		}
		return air;
	}
	/**
	 * computes the number of edges that don't respect the hierarchy
	 * @return
	 */
	public int nbNonHierarchical(){
		int nb = 0;
		for(int i = 0; i < Edges.size(); i++ ){
			Edge ei = (Edge) Edges.get(i);
			if(ei.start.y > ei.end.y)
				nb++;
		}
		return nb;
	}
	/**
	 * computes the number of crossings between an edge and a vertice
	 * @return
	 */
	public int nbCrossEdgeVertice(){
		int nb = 0;
		for(int i = 0; i < Edges.size(); i++ ){
			Edge ei = (Edge) Edges.get(i);
			Line2D.Double line = new Line2D.Double(ei.end.x + (double)ei.end.width/2,
					ei.end.y + (double)ei.end.height/2, ei.start.x + (double)ei.start.width/2,
					ei.start.y + (double)ei.start.height/2);
			for(int j = 0; j < Vertices.size(); j++){
					Vertice vj = (Vertice)Vertices.get(j);
					if(ei.start.index != vj.index && ei.end.index != vj.index){
						Rectangle2D.Double recj = new Rectangle2D.Double(vj.x, vj.y, vj.width, vj.height);
						if(recj.intersectsLine(line))
							nb ++;
					}
			}
		}
		return nb;
	}
	/**
	 * evaluates the graph
	 * @return
	 */
	public String evaluate(){
		return ""+nbCrossing()+";"+nbCrossEdgeVertice()+";"+edgesLength()+";"+superpositionAir()+";"+nbNonHierarchical();
	}
	
}
