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

public class Graph_Decision 
{
    private String            				name_decision;
    private ArrayList<DecisionObj>   		decisions;
  
    
    public Graph_Decision() 
    {
    	this("", new ArrayList<DecisionObj>()); 	
    }

    public Graph_Decision(String name, ArrayList<DecisionObj> initial) 
    {
    	name_decision = name;
        decisions = initial;
    }
  
    public void addDecision(DecisionObj obj_decision) 
    {
    	decisions.add(obj_decision);
    }
    
    public ArrayList<DecisionObj> getDecisions() 
    { 
    	return decisions; 
   	}
    
/*    
    public void draw(Graphics2D g2)
    {
       for (DecisionObj n : decisions)
       {	   
          n.draw(g2); 
       }   

    }
*/    
    public void draw(Graphics2D g2)
    {
     
       
       for (DecisionObj obj : decisions)
       {	   
          obj.draw(g2); 
       }   
       
      // ArrayList<Edge_transition>    edges = getEdges();
       
      // for (Edge_transition e: edges)  
      // {
    //	   e.draw_transistion_decision(g2);
     //  }

    }

    
    public DecisionObj decisionAt(int x, int y) 
    {
   	
        for (int i = 0;  i< decisions.size() ; i++)
        {
            DecisionObj decision_obj = decisions.get(i); 
            
            int x1 = decision_obj.getP1().x;
            int y1 = decision_obj.getP1().y;

            int x2 = decision_obj.getP2().x;
            int y2 = decision_obj.getP2().y;

            int x3 = decision_obj.getP3().x;
            int y3 = decision_obj.getP3().y;

            int x4 = decision_obj.getP4().x;
            int y4 = decision_obj.getP4().y;

            
          	// triangle 1 : P1 P2 P3
          	boolean is_inside_triangle1 = isInside(x1 , y1, x2 , y2 , x3 , y3 , x,  y); 
          	// triangle 1 : P1 P4 P3
      		boolean is_inside_triangle2 = isInside(x1 , y1, x4 , y4 , x3 , y3 , x,  y); 
      	
      		if( is_inside_triangle1  == true || is_inside_triangle2 == true )
      		{
      			System.out.println("Decision inside");
      			return decision_obj;
      		}
      		//else
      		//{	
      		//	System.out.println("Decision outside");
      		//	return(null);
      		//}	
          
        }
		return null;
        
    }
 
    public boolean isInside(int x1, int y1, int x2, int y2, int x3, int y3, int x, int y) 
    {    
    	/* Calculate area of triangle ABC */
    	double A = triangle_area (x1, y1, x2, y2, x3, y3); 
   
    	/* Calculate area of triangle PBC */  
    	double A1 = triangle_area (x, y, x2, y2, x3, y3); 
   
    	/* Calculate area of triangle PAC */  
    	double A2 = triangle_area (x1, y1, x, y, x3, y3); 
   
    	/* Calculate area of triangle PAB */   
    	double A3 = triangle_area (x1, y1, x2, y2, x, y); 
     
    	/* Check if sum of A1, A2 and A3 is same as A */
    	return (A == A1 + A2 + A3); 
    } 
   
    
    public double triangle_area(int x1, int y1, int x2, int y2,  int x3, int y3) 
    { 

    	return Math.abs((x1*(y2-y3) + x2*(y3-y1)+ x3*(y1-y2))/2.0); 
    } 
    
    public void addEdge(DecisionObj obj1, DecisionObj obj2, Point p1, Point p2, int indice_start, int indice_end) 
    {
    	Edge_transition anEdge = new Edge_transition(obj1, obj2,  p1, p2 , indice_start,  indice_end);
        obj1.addIncidentEdge(anEdge);
        obj2.addIncidentEdge(anEdge);
    }
    
    public boolean buildEdge( Point p1, Point p2) 
    {
    	DecisionObj obj1 = findNode(p1);
    	DecisionObj obj2 = findNode(p2);
       
    	if (obj1 != null && obj2 != null)
    	{
    		// detection des points d'ancrage	
            Point pt_ancre_start = new Point(); 
            Point pt_ancre_end = new Point(); 
    		
            List<Object> l1 = calcul_ancrage(obj1, p1);
            pt_ancre_start = (Point) l1.get(0);
            int indice_start = (int) l1.get(1);
            
            List<Object> l2 = calcul_ancrage(obj2, p2);
            pt_ancre_end = (Point) l2.get(0);
            int indice_end = (int) l2.get(1);
            
    		addEdge(obj1, obj2 , pt_ancre_start, pt_ancre_end , indice_start, indice_end);
    	   
    //		ArrayList<Edge_transition>    edges = xxxgetEdges();
    	   
    		System.out.println(" EDGE DECISION ADDED " + pt_ancre_start + " " + indice_start + " " + pt_ancre_end + " " + indice_end); 
    		
    		return true;
    	}
    	
    	return false;
    }
    
    
    public boolean xxx( Point p1, Point p2, Node_Rectangular NodeStart, Node_Rectangular NodeEnd , DecisionObj DecisionStart , DecisionObj DecisionEnd) 
    {
    	
        	Point pt_ancre_start = new Point(); 
        	Point pt_ancre_end = new Point(); 
    	
			if(NodeStart != null && NodeEnd != null) // 2 nodes
			{
				return true;
			}
			
			if(DecisionStart != null && DecisionEnd != null)  // 2 decision
			{
				return true;
			}
			
			if(NodeStart != null  && DecisionEnd != null )  // 
			{
				return true;
			}
			
			if( DecisionStart != null && NodeEnd != null)
			{
				
				 List<Object> l1 = calcul_ancrage(DecisionStart, p1);
		         pt_ancre_start = (Point) l1.get(0);
		         int indice_start = (int) l1.get(1);
		           
		         pt_ancre_end.setLocation( NodeEnd.getX1() + NodeEnd.getWidth() / 2 , NodeEnd.getY1()); 
		         
		         // mixte   
		    	 //addEdge(obj1, obj2 , pt_ancre_start, pt_ancre_end , indice_start, indice_end);
		         
		         Edge_transition anEdge = new Edge_transition(DecisionStart, NodeEnd,  pt_ancre_start, indice_start,  pt_ancre_end  );
		         
		         
		         DecisionStart.addIncidentEdge(anEdge);
		         NodeEnd.addIncidentEdge(anEdge);
		         
		         // Now tell the nodes about the edge
		         //start.addIncidentEdge(anEdge);
		         //end.addIncidentEdge(anEdge);
		    	   
		    	 ArrayList<Edge_transition>    edges = getEdges();
		    	   
		    	
		    	 
		    	 System.out.println(" EDGE DECISION ADDED " + pt_ancre_start + " " + indice_start + " " + pt_ancre_end ); 
		    		
				
				//return true;
			}
    	
			return false;
    }
    
   
    
    public DecisionObj findNode(Point2D p)
    {
    	return decisionAt( (int)p.getX(), (int)p.getY()); 
    }
    
  
    public ArrayList<Edge_transition> getEdges() 
    {
        ArrayList<Edge_transition>  edges = new ArrayList<Edge_transition>();

        for (DecisionObj obj: decisions) 
        {
            for (Edge_transition e: obj.incidentEdges()) 
            {
                if (!edges.contains(e)) 
                {	
                    edges.add(e);
                }
            }
        }

        return edges;
    }
    
    
    
    
    public ArrayList<Edge_transition> xxxgetEdges()  
    {
        ArrayList<Edge_transition>  edges = new ArrayList<Edge_transition>();

        for (DecisionObj obj: decisions) 
        {
            for (Edge_transition e: obj.incidentEdges()) 
            {
                if (!edges.contains(e)) 
                {	
                    edges.add(e);
                }
            }
        }

        return edges;
    }
    
    public List<Object> calcul_ancrage(DecisionObj obj, Point pt)
    {
    	   int x1 = obj.getP1().x;
           int y1 = obj.getP1().y;

           int x2 = obj.getP2().x;
           int y2 = obj.getP2().y;

           int x3 = obj.getP3().x;
           int y3 = obj.getP3().y;

           int x4 = obj.getP4().x;
           int y4 = obj.getP4().y;
        
           //double distance = Math.sqrt( (y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
           				 	 Math.sqrt(  (y2 - y1)   * (y2 - y1)   + (x2 - x1)   * (x2 - x1)  );
           
           double distance1 = Math.sqrt( (y1 - pt.y) * (y1 - pt.y) + (x1 - pt.x) * (x1 - pt.x));
           
           double distance2 = Math.sqrt( (y2 - pt.y) * (y2 - pt.y) + (x2 - pt.x) * (x2 - pt.x));
           
           double distance3 = Math.sqrt( (y3 - pt.y) * (y3 - pt.y) + (x3 - pt.x) * (x3 - pt.x));
           
           double distance4 = Math.sqrt( (y4 - pt.y) * (y4 - pt.y) + (x4 - pt.x) * (x4 - pt.x));
           
        
           double[] dist_array = { distance1, distance2, distance3 , distance4};
           
           double value = getMin(dist_array);
           
           int indice = SmallestNumberIndex(dist_array);
   
           Point pt_ancre = new Point();
           
           List listSource = new ArrayList();
           
           switch (indice) 
           {
	           case 1:  pt_ancre = obj.getP1();
	                    break;
	           case 2:  pt_ancre = obj.getP2();
	                    break;
	           case 3:  pt_ancre = obj.getP3();
	                    break;
	           case 4:  pt_ancre = obj.getP4();
	                    break;
           }
           
           //retour de la position pour connaitre le pt : p1, p2, p3 ou p4
           // http://tutorials.jenkov.com/java-collections/list.html
           https://www.geeksforgeeks.org/returning-multiple-values-in-java/
           listSource.add(pt_ancre);
           listSource.add(indice);
           
          // return pt_ancre;
           return listSource;
    }
    
    public double getMin(double[] inputArray)
    { 
        double minValue = inputArray[0]; 
        
        for(int i=1;i<inputArray.length;i++)
        { 
        	if(inputArray[i] < minValue)
        	{ 
        		minValue = inputArray[i]; 
        	} 
        } 
        
        return minValue; 
    } 
    
   
    public int SmallestNumberIndex (double[] inputArray ) 
    {
    	double a[] = inputArray;
   	    double min = a[0];
   	    int index=0;

   	    for(int i = 0; i < a.length; i++)
   	    {
   	        if(min > a[i])
   	        {
   	            min = a[i];
   	            index = i+ 1;
   	        }
   	    }

   	    return index ;
   	}
    
}   