import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.*;

public class Graph 
{
    private String            label;
    private ArrayList<Node>   nodes;
    
	private ArrayList<PolygonSommet> list_polygon_sommet = new ArrayList<PolygonSommet>();

	private ArrayList<PolygonObj> list_polygon_obj = new ArrayList<PolygonObj>();

    
    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();
    }
 
    
    public void addPolygonSommet(PolygonSommet aPolygon) 
    {
    	list_polygon_sommet.add(aPolygon);
    }
    
   
    public void draw_polygon_sommet(Graphics g)
    {
    	
    	 for (PolygonSommet obj: list_polygon_sommet)   
         {	
             obj.draw_polygon_sommet(g);
         }   
    	
    	 
    	 // https://www.developer.com/net/vb/article.php/874351/A-Simple-Java-Drawing-Tool.htm
    	 
    	 // https://introcs.cs.princeton.edu/java/stdlib/Draw.java.html
/*    	 
    	 Polygon p = new Polygon();
    	 
    	 for(int i = 0; i < list_polygon_sommet.size() ; i++)
    	 {
    		 p.addPoint(list_polygon_sommet.get(i).getX(), list_polygon_sommet.get(i).getY());   
    	 } 	 
    	 
    	 PolygonSommet obj = new PolygonSommet();
    	 obj.draw_polygon_segment(g, p); 
*/   

    	
    }
    
    
    public void draw_polygon(Graphics g)
    {
    	
    	int x1 = 0, y1 = 0;
    	
	   	for(int i = 0; i < list_polygon_obj.size() ; i++)
	   	{
	   			ArrayList<Point> array_list_point = list_polygon_obj.get(i).get_polygon_points();

	   			// remise à z&ro pour chaque polygon
	   			int ix[], iy[];
	   			ix = new int[array_list_point.size()];
	   			iy = new int[array_list_point.size()];
	     
	   			for(int j = 0; j < array_list_point.size() ; j++)
	   			{
	   				   if( j == 0)
	   			       {
	   					   x1 = (int)array_list_point.get(0).getX();
	   					   y1 = (int)array_list_point.get(0).getY();
	   			       }
	   				
	   				  g.setColor(Color.black);
	   				  g.fillOval((int)array_list_point.get(j).getX() - 5, (int)array_list_point.get(j).getY() - 5, 10, 10);
	   				
	   				  g.setColor(Color.blue);
	   				  if( j < array_list_point.size() - 1 )
	  		   	      {   
	   				      // trace les N - 1 segments
	   					  g.drawLine( 	(int)array_list_point.get(j).getX(), (int)array_list_point.get(j).getY(), 
	   							  		(int)array_list_point.get(j+1).getX(), (int)array_list_point.get(j+1).getY()
	   							  	);
	  		   	      }
	   				  else
	   				  {	  // on trace le dernier segments qui se reboucle sur le premier point
	   					  g.drawLine( 	(int)array_list_point.get(j).getX(), (int)array_list_point.get(j).getY(), 
 							  		    x1, y1
 							  	);
	   				  }
	   				  
			   		  /*if( j < array_list_point.size() - 1 )
				   	  {   
			   			  if( j == 0)
			   			  {	   
			   				  // stocke le premier point	
			   				   x1 = (int)array_list_point.get(0).getX();
			   				   y1 = (int)array_list_point.get(0).getY();
			   			  }
			   			  ix[j] = (int)array_list_point.get(j).getX();
			   			  iy[j] = (int)array_list_point.get(j).getY();
				   	  }
			   		  else
			   		  {   
			   			  // le dernier point est identique au premier point
			   			  ix[j] = x1;
			   			  iy[j] = y1;
			   		 }*/
	   		  
	   			} 
	    
	   			/*g.setColor(Color.blue);
	   			/*g.drawPolygon(ix, iy , ix.length);
	   	  
	   			
	   			
	   			
	   	} 	
    	
    }
   
    
    public void xxxxxxdraw_polygon(Graphics g)
    {
    	
    	Polygon p = new Polygon();
	   	
	   	for(int i = 0; i < list_polygon_obj.size() ; i++)
	   	{
	   	  ArrayList<Point> array_list_point = list_polygon_obj.get(i).get_polygon_points();
	   	  
	   	  System.out.println(" Arraylist points " + array_list_point);
	   	  
	   	  for(int j = 0; j < array_list_point.size() ; j++)
	   	  {
	   		 p.addPoint( (int)array_list_point.get(j).getX(), (int)array_list_point.get(j).getY()); 
	   		 
	   		 System.out.println(" +++ " + array_list_point.get(j).getX() + " " + (int)array_list_point.get(j).getY() );
	   		  
	   		 g.setColor(Color.red);
	         g.fillOval((int)array_list_point.get(j).getX() - 5, (int)array_list_point.get(j).getY() - 5, 10, 10);
	         
	         g.setColor(Color.black);
	   		  
	         
	   	  } 	
	   	  
	   	  int x1 = 0, y1 = 0;
	   	  
	   	 int xPoints[] = {  };
	     int yPoints[] = {  };
	     
	     double[] myList = new double[10];
	   	  
	     myList[0] = 5;
	     myList[1] = 175;
	     
	     List<Integer> xp = new ArrayList<Integer>();
	     List<Integer> yp = new ArrayList<Integer>();
	     
	     List<Integer> list = new ArrayList<Integer>();
	     list.add(1);
	     list.add(2);
	     list.add(3);
	     
	 
	     
	     int ix[], iy[];
	     
	     ix = new int[array_list_point.size()];
	     iy = new int[array_list_point.size()];
	     
	   	 for(int j = 0; j < array_list_point.size() ; j++)
	   	 {
	   		 
	   		  if( j < array_list_point.size() - 1 )
		   	  {   
	   			  if( j == 0)
	   			  {
	   				   x1 = (int)array_list_point.get(0).getX();
	   				   y1 = (int)array_list_point.get(0).getY();
	   				  
	   			  }
	   			  //g.drawLine( (int)tgb.get(j).getX(), (int)tgb.get(j).getY(), (int)tgb.get(j+1).getX(), (int)tgb.get(j+1).getY());
	   			//xPoints[j] = (int)tgb.get(j).getX();
	   			//xPoints[j] = (int)tgb.get(j).getY();
	   			
	   			xp.add( (int)array_list_point.get(j).getX() );
	   			yp.add( (int)array_list_point.get(j).getY() );
	   			
	   			ix[j] = (int)array_list_point.get(j).getX();
	   			iy[j] = (int)array_list_point.get(j).getY();
   			}
   		  else
	   		  {
	   			  //g.drawLine( (int)tgb.get(j).getX(), (int)tgb.get(j).getY(), x1, y1);
	   			//xPoints[j] = x1;
	   			//xPoints[j] = y1;
	   			
	   			xp.add( x1 );
	   			yp.add( y1 );
	   			
	   			ix[j] = x1;
	   			iy[j] = y1;
	   			
	   		  }
	   		  
	   	  } 
	   	  
	   	 Integer[] aaa = xp.toArray(new Integer[0]);
	     Integer[] bbb = yp.toArray(new Integer[0]);
	   	 
	     
	     Integer[] xxx = new Integer[xp.size()];
	        for (int k = 0; k < xp.size(); k++) 
	        {
	           xxx[k] = xp.get(k);
	        }
	   	 
	        Integer[] yyy = new Integer[yp.size()];
	        for (int k = 0; k < yp.size(); k++) 
	        {
	           yyy[k] = yp.get(k);
	        }
	   	    
	        
	    // Object[] xArray = xp.toArray();
	    // Object[] yyArray = yp.toArray();
	     
	   
	    
	   	 g.drawPolygon(ix, iy , ix.length);
	   	// g.drawPolygon(xPoints, yPoints, xPoints.length);
	   	 
	   	 System.out.println("  points " + p);
	   	 
//	   	 g.drawPolygon(p); 
/*	   	 
	   	 int xPoints[] = { x, 200, x, 200 };
	     int yPoints[] = { y, 230, 200, y };
	     GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
	         xPoints.length);
	     polygon.moveTo(xPoints[0], yPoints[0]);
	     for (int index = 1; index < xPoints.length; index++) {
	       polygon.lineTo(xPoints[index], yPoints[index]);
	     }
	     polygon.closePath();

	     g2.draw(polygon);
	*/   	 
	   	 
	   	//tgb.size()
	   	 
	     //g.drawPolygon(xpoints, ypoints, npoints);
	   	  
	   	//  PolygonSommet obj = new PolygonSommet();
	   	//  obj.draw_polygon_segment(g, p); 
	   	  
	   	} 	
    	
    }
    
    public void draw_polygon_segment(Graphics g)
    {
    	/*
    	Polygon p = new Polygon();
   	 
	   	for(int i = 0; i < list_polygon_sommet.size() ; i++)
	   	{
	   		p.addPoint(list_polygon_sommet.get(i).getX(), list_polygon_sommet.get(i).getY());   
	   	} 	 
	   	 
	   	PolygonSommet obj = new PolygonSommet();
	   	obj.draw_polygon_segment(g, p); 
	   	*/
	   	
  
    	Polygon p = new Polygon();
	   	
	   	for(int i = 0; i < list_polygon_obj.size() ; i++)
	   	{
	   	  ArrayList<Point> tgb = list_polygon_obj.get(i).get_polygon_points();
	   	  
	   	  for(int j = 0; j < tgb.size() ; j++)
	   	  {
	   		  p.addPoint( (int)tgb.get(j).getX(), (int)tgb.get(j).getY());   
	   	  } 	 
	   	 
	   	  PolygonSommet obj = new PolygonSommet();
	   	  obj.draw_polygon_segment(g, p); 
	   	  
	   	} 	
	  
    }
        
    public void addPolygon(PolygonObj apolygon) 
    {
    	list_polygon_obj.add(apolygon);
    }
    
    public ArrayList<Point> get_list_polygon_sommet()
    {
    	  ArrayList<Point> points_array = new ArrayList<Point>();	

		  Point pt1 = new Point();
		  
    	  for(int i = 0; i < list_polygon_sommet.size() ; i++)
    	  {
    		  
    		  Point ptx = new Point();
    		  int x, x1;
        	  int y, y1;
    		  
    		 // int x = list_polygon_sommet.get(i).getX();
    		 // int y = list_polygon_sommet.get(i).getY();
    		  
    		  if(i ==  0 )
    		  {
    			  x1 = list_polygon_sommet.get(i).getX();
    			  y1 = list_polygon_sommet.get(i).getY();
    			  pt1.setLocation(x1, y1);
    		  }	  

			  x = list_polygon_sommet.get(i).getX();
			  y = list_polygon_sommet.get(i).getY();

   		  
    		  ptx.setLocation(x, y);
    		  
    		  points_array.add( ptx );
    		  
    		  //if(i ==  list_polygon_sommet.size() - 1 )
    		  //{
    		  //	  points_array.add( pt1 );
    		  //}
    		  
    	  }
    	  return points_array;
    }
    
    
    public void clear_list_polygon_sommet()
    {
    	list_polygon_sommet.clear();
    	//System.out.println(" Taille du buffer :  " + list_polygon_sommet.size());
    }
    
    public  PolygonObj PolyhonAt(int x, int y)
    {
    	
    	int SIZE = 10;
    	
    	Point p = new Point();
    	
    	p.setLocation(x, y);
    	
    	for (int i = 0;  i< list_polygon_obj.size() ; i++)
        {
            PolygonObj item = list_polygon_obj.get(i); 
            ArrayList<Point> tgb  = item.get_polygon_points();
            //ArrayList<Point> tgb = list_polygon_obj.get(i).get_polygon_points();
  	   	  
  	   	    for(int j = 0; j < tgb.size() ; j++)
  	   	    {
  	   	    	int xmin = (int) tgb.get(j).getX() - SIZE / 2;
  	   	    	int ymin= (int) tgb.get(j).getY() - SIZE / 2;		 
  	   	    	int xmax = xmin + SIZE;
  	   	    	int ymax = ymin + SIZE;
	   	    	
  	   	    	if(x >= xmin && x <= xmax && y >= ymin && y <= ymax  )
  	   	    	{
  	   	    		item.SetSelectedSommet(true);
  	   	    		item.SetSelectedSommetIndice(j);
  	   	    		return item;
  	   	    	}
  	   	    } 
        }
    	
    	return null;
    }	
    
    public  void PolyhonSetPointSommet(PolygonObj obj, int indice, int x, int y)
    {
    	Point pt = new Point();
    	
    	Point p = new Point();
        
        pt.setLocation(new Point(x,y));
    	
    	
    	ArrayList<Point> array_list_point = obj.get_polygon_points();
    	
    	int nb_points = array_list_point.size();
    	
    /*	if(indice == 0 || indice == nb_points)
    	{
    		array_list_point.set(0,  pt );
    		array_list_point.set(nb_points,  pt );
    	}
    	
    	else
    	{	
    		array_list_point.set(indice,  pt );
    	}
    */	
    	array_list_point.set(indice,  pt );
    	
    	
    }
    
 /*   
    public void polygon(double[] x, double[] y) {
        int n = x.length;
        GeneralPath path = new GeneralPath();
        path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
        for (int i = 0; i < n; i++)
            path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
        path.closePath();
        offscreen.draw(path);
        draw();
    }
    
    ou bien
    
     int xPoints[] = { x, 200, x, 200 };
    int yPoints[] = { y, 230, 200, y };
    GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
        xPoints.length);
    polygon.moveTo(xPoints[0], yPoints[0]);
    for (int index = 1; index < xPoints.length; index++) {
      polygon.lineTo(xPoints[index], yPoints[index]);
    }
    polygon.closePath();

    g2.draw(polygon);
    
*/    
    
}

