import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.io.*;
import java.util.*;

public class Graph 
{
    private String            label;
    private ArrayList<Node>   nodes;

    public Graph() 
    { 
    	this("", new ArrayList<Node>()); 
  	}

    public Graph(String aLabel) 
    { 
    	this(aLabel, new ArrayList<Node>()); 
   	}

    public Graph(String aLabel, ArrayList<Node> initialNodes) 
    {
        label = aLabel;
        nodes = initialNodes;
    }
    
    public ArrayList<Node> getNodes() { return nodes; }
    public String getLabel() { return label; }
    public void setLabel(String newLabel) { label = newLabel; }

    public String toString() 
    {
        return(label + "(" + nodes.size() + " nodes, " + getEdges().size() + " edges)");
    }
    
    public ArrayList<EdgeObj> getEdges() 
    {
        ArrayList<EdgeObj>  edges = new ArrayList<EdgeObj>();

        for (Node n: nodes) 
        {
            for (EdgeObj e: n.incidentEdges()) 
            {
                if (!edges.contains(e)) 
                {	
                    edges.add(e);
                }    
            }
        }

        return edges;
    }

    public void addNode(Node aNode) 
    {
        nodes.add(aNode);
    }

    public void addEdge(Node start, Node end) 
    {
        EdgeObj anEdge = new EdgeObj(start, end);

        start.addIncidentEdge(anEdge);
        end.addIncidentEdge(anEdge);
    }

    
    public void addEdge(Node start, Node end, String equation) 
    {
        EdgeObj anEdge = new EdgeObj(start, end, equation);

        start.addIncidentEdge(anEdge);
        end.addIncidentEdge(anEdge);
    }
    
    
    
    public void addEdge(String startLabel, String endLabel) 
    {
        Node start = nodeNamed(startLabel);
        Node end = nodeNamed(endLabel);
        if ((start != null) && (end != null))
            addEdge(start, end);
    }
    
    public void deleteEdge(EdgeObj anEdge) 
    {
        anEdge.getStartNode().incidentEdges().remove(anEdge);
        anEdge.getEndNode().incidentEdges().remove(anEdge);
    }

    public void deleteNode(Node aNode) 
    {
        for (EdgeObj e:   aNode.incidentEdges())
        {	
            e.otherEndFrom(aNode).incidentEdges().remove(e);
        }   

        nodes.remove(aNode);  
    }

    public Node nodeNamed(String aLabel) 
    {
        for (Node n:  nodes)
        {	
            if (n.getLabel().equals(aLabel))  return n;
        }    

        return null;  
    }
    
    public int nodePosition(String aLabel) 
    {
    	int cpt = 0;
    	
        for (Node n:  nodes)
        {	
            if (n.getLabel().equals(aLabel)) 
            {	
            	return cpt;
            }
            cpt = cpt + 1;
        }    
            
        return cpt;  
    }
    
    public static Graph example() 
    {
        Graph    myMap = new Graph("Dev and debug FSM ");
        myMap.addNode(new Node("State0", new Point2D.Double(250,100)));
        myMap.addNode(new Node("State1", new Point2D.Double(400,350)));
        return myMap;
    }

    public void draw(Graphics g) 
    {
        ArrayList<EdgeObj>    edges = getEdges();
        
        for (EdgeObj e: edges)  
        {
            e.draw(g);
        }

        for (Node n: nodes)   
        {	
            n.draw(g);
        }   
    }
    
    public void effacer_tout() 
    {
        ArrayList<EdgeObj>    edges = getEdges();
        
        edges.clear();
        nodes.clear();
    }
  
    public Node nodeAt(double x, double y) 
    {
        for (int i=nodes.size()-1; i>=0; i--)
        {
            Node n = nodes.get(i);
            Point2D c = n.getLocation();
            double d = (x - c.getX()) * (x - c.getX()) +  (y - c.getY()) * (y - c.getY());
            if (d <= (Node.RADIUS*Node.RADIUS))
                return n;
        }
        return null;
    }

    public EdgeObj edgeAt(double x, double y) 
    {
        for (EdgeObj e:  getEdges()) 
        {
            Node n1 = e.getStartNode();
            Node n2 = e.getEndNode();
            double xDiff = n2.getLocation().getX() - n1.getLocation().getX();
            double yDiff = n2.getLocation().getY() - n1.getLocation().getY();
            double distance = Math.abs(xDiff*(n1.getLocation().getY() - y) -  (n1.getLocation().getX() - x)*yDiff) /   Math.sqrt(xDiff*xDiff + yDiff*yDiff);
            
            if (distance <= 5) 
            {
                if (Math.abs(xDiff) > Math.abs(yDiff)) 
                {
                    if (((x < n1.getLocation().getX()) && (x > n2.getLocation().getX())) || ((x > n1.getLocation().getX()) && (x < n2.getLocation().getX())))
                        return e;
                }
                else
                if (((y < n1.getLocation().getY()) && (y > n2.getLocation().getY())) ||  ((y > n1.getLocation().getY()) && (y < n2.getLocation().getY())))
                    return e;
            }
        }
        return null;
    }

    public EdgeObj textEdgeAt(double x, double y) 
    {
    	for (EdgeObj e:  getEdges()) 
        {
    		int x1 = e.text_x;
        	int y1 = e.text_y - e.height_string;
        	int x2 = x1 + e.width_string;
        	int y2 = e.text_y; 
        	
        	if( x >= x1 && x <= x2 && y >= y1 && y <= y2  )
            {
        		return e;
            }
        } 
    	return null;
    }

    public ArrayList<Node> selectedNodes() 
    {
        ArrayList<Node>   selected = new ArrayList<Node>();
        for (Node n:  nodes)
        {	
            if (n.isSelected()) selected.add(n);
        }
        return selected;
    }
    
    public String find_selected()
    {
        int position = 0;
        
        for (Node n:  nodes)
        {    
            if (n.isSelected())
            {     
                  String label =  n.getLabel();
                  String retour= label; // + "-" + Integer.toString(position) ;
                  return(retour);  
            } 
            position = position + 1;
        }          
        
        return(null);
    }
    
    public ArrayList<EdgeObj> selectedEdges() 
    {
        ArrayList<EdgeObj>   selected = new ArrayList<EdgeObj>();
        for (EdgeObj e:  getEdges())
        {
        	if (e.isSelected()) selected.add(e);
        }
        return selected;
    }

    public void saveTo(PrintWriter aFile) 
    {
        aFile.println(label);

        aFile.println(nodes.size());
        for (Node n:  nodes)
        {	
            n.saveTo(aFile);
        }
        
        ArrayList<EdgeObj> edges = getEdges();
        aFile.println(edges.size());
        for (EdgeObj e:  edges)
        {	
            e.saveTo(aFile);
        }    
    }

    public static Graph loadFrom(BufferedReader aFile) throws IOException 
    {
        Graph    aGraph = new Graph(aFile.readLine());

        int numNodes = Integer.parseInt(aFile.readLine());
        for (int i=0; i<numNodes; i++)
            aGraph.addNode(Node.loadFrom(aFile));

        int numEdges = Integer.parseInt(aFile.readLine());
        for (int i=0; i<numEdges; i++) 
        {
            EdgeObj tempEdge = EdgeObj.loadFrom(aFile);
            Node start = aGraph.nodeAt(tempEdge.getStartNode().getLocation().getX(),  tempEdge.getStartNode().getLocation().getY());
            Node end = aGraph.nodeAt(tempEdge.getEndNode().getLocation().getX(),  tempEdge.getEndNode().getLocation().getY());
            aGraph.addEdge(start, end , tempEdge.getEquation()); 
        }
        
        return aGraph;
    }
    
    public void new_draw(Graphics2D g, boolean recalculation) 
    {
        ArrayList<EdgeObj>    edges = getEdges();
        
        for (EdgeObj e: edges)  
        {	
        	 e.new_draw(g, recalculation);
        }
        
        for (Node n: nodes) 
        {	
            n.draw(g );
        }
    }
        
    public void dynamic_draw(Graphics2D g ) 
    {
        ArrayList<EdgeObj>    edges = getEdges();
        
        for (EdgeObj e: edges)  
        {	
        	 e.dynamic(g); 
        }
        
        for (Node n: nodes)   
        {
        	n.draw(g);
        }
    }
    
    public EdgeObj New_edgeAt(double x, double y) 
    {
        for (EdgeObj e:  getEdges()) 
        {
        	if( e.detection_transistion( (int) x, (int) y) == true )
        	{
        		System.out.println("Transaction selected");
        		return e;
        	}
        }
        return null;
    }
    
    public boolean GetEdgeExists(Node start, Node end) 
    {
    	String new_start = start.getLabel();
    	String new_end = end.getLabel();
    	ArrayList<EdgeObj>    edges = getEdges();
    	
    	if(edges.size() != 0)
    	{
	    	for (EdgeObj e:  edges) 
	    	{
	    		String exists_start  = e.getStartNode().getLabel();
	    		String exists_end  = e.getEndNode().getLabel();
	        	
	    		if( new_start.equals(exists_start)  && new_end.equals(exists_end) )        	
	    		{
	    			return true;
	    		}
	    	}
    	}	
    	
        return false;
    }
   
    public LoopbackObj LoopAt(int x, int y) 
    {
        for (Node n:  nodes) 
        {
        	LoopbackObj loopback_obj = new LoopbackObj();
        	loopback_obj = n.getLoopBack();
        	
        	if( loopback_obj.detection_loop(x, y) == true )
        	{
            	return loopback_obj;
        	}
        }
        return null;
    }
    
    public void recalculation(Node selected ) 
    {
        
        selected.recalculation();
    }
 
}

