import java.awt.event.*;
import java.awt.*;

/**This class fits a piece of the monochrome tiling to the triangl filler.
The idea is that the ShapeRecognizer class finds the curve in the abstract
but then we want to map it onto the actual triangulation*/


public class PuzzleFitter {


    /**This is the main routine*/

    /**This finds a fitting of a geometric piece on top of the triangle filler.
       The variable FUDGE is an extra variable that I have added because I got too frustrated
       with fixing the orientation.*/
    

    public static PolygonWrapper fit(TriangleFiller X,EnhancedPolygon E,int[][] piece,int FUDGE) {
	if(piece==null) return null;
	if(piece[0].length==1) return fit1(X,E,piece);
	if(piece[0].length==2) return fit2(X,E,piece);
	int index=firstIndex(piece,3);
	if(index>=0) return fitGeneric(X,E,piece,FUDGE);
	else fitHex(X,E,piece);
	return null;
      }
    
    
    /**This handles the case where there is at least one 3-vertex on the boundary*/

    public static PolygonWrapper fitGeneric(TriangleFiller X,EnhancedPolygon E,int[][] piece,int FUDGE) {
	
	PolygonWrapper P=ShapeRecognizer.boundaryRealization(piece);
        int index=firstIndex(piece,3);
	
  	int vtx=piece[1][index];
	int[] f=ShapeRecognizer.incidentTriangles(E,piece,index);
	if(f==null) return P;
	f=tripleToDouble(E,f);
	if(f.length<2) return P;

	int[] g=findVertexSharingTriangles(X,f);
	Complex[] Z1=X.TRI[g[0]];
	Complex[] Z2=X.TRI[g[1]];
	int i1=matchIndex(Z1,Z2);
	int i2=farIndex(Z1,Z2);

	Complex z0=new Complex(P.z[index]);
	Complex z1=Z1[i1];
	Complex z2=Z1[i2];
	Complex z12=Complex.minus(z2,z1);

	Complex w0=new Complex(P.z[index]);
	Complex w1=P.z[index];
	Complex w2=P.z[(index+1)%P.count];
	Complex w12=Complex.minus(w2,w1);

	Complex[] H=new Complex[P.count];
	for(int i=0;i<H.length;++i) H[i]=new Complex(P.z[i]);
	for(int i=0;i<H.length;++i) H[i]=Complex.minus(H[i],w0);
       	for(int i=0;i<H.length;++i) H[i]=Complex.divide(H[i],w12);

	if(FUDGE==1) {
	    for(int i=0;i<H.length;++i) H[i]=new Complex(H[i].x,-H[i].y);
	}

	Complex[] H2=new Complex[H.length];
	for(int i=0;i<H.length;++i) H2[i]=new Complex(-H[i].x,-H[i].y);
	
	

       	for(int i=0;i<H.length;++i) H[i]=Complex.times(H[i],z12);
       	for(int i=0;i<H.length;++i) H[i]=Complex.plus(H[i],z1);

       	for(int i=0;i<H.length;++i) H2[i]=Complex.times(H2[i],z12);
       	for(int i=0;i<H.length;++i) H2[i]=Complex.plus(H2[i],z1);

	PolygonWrapper Q=new PolygonWrapper(H.length,H);
	PolygonWrapper Q2=new PolygonWrapper(H2.length,H2);

	Complex z=TriangleFiller.center(Z1);
	if(Q.contains(z)==true) return Q;
	return Q2;
    }
    

    /**This handles the case when the monochrome region is a 6-piece hexagon*/
    public static PolygonWrapper fitHex(TriangleFiller X,EnhancedPolygon E,int[][] piece) {
  	PolygonWrapper P=ShapeRecognizer.boundaryRealization(piece);
	System.out.println("used");
	return P;
    }

    /**This handles the rhombus case*/
    public static PolygonWrapper fit2(TriangleFiller X,EnhancedPolygon E,int[][] piece) {
        int[] G=piece[2];
        int index=-1;
	for(int i=0;i<G.length;++i) {
	    if(G[i]==2) index=i;
	}
	int[] f=ShapeRecognizer.incidentTriangles(E,piece,index);
	int[] g=findAdjacentTriangles(X,f);
	if(g==null) return null;
	Complex[] Z1=X.TRI[g[0]];
	Complex[] Z2=X.TRI[g[1]];
	int i1=outIndex(Z1,Z2);
	int i2=outIndex(Z2,Z1);
	int j1=(i1+1)%3;
	int k1=(j1+1)%3;
	Complex[] Q={Z1[i1],Z1[j1],Z2[i2],Z1[k1]};
	PolygonWrapper P=new PolygonWrapper(4,Q);
	return P;
      }

    /**This takes a triple of triangles and finds the two indices
       in which the triangle is not adjecent to the two others.
       So, the corresponding triangles here should share a vertex*/

    public static int[] tripleToDouble(EnhancedPolygon E,int[] f) {
	int count=0;
	int[] list=new int[2];
	for(int i=0;i<3;++i) {
	    int[] g=ShapeRecognizer.adjacentMonochrome(E,f[i]);
	    g=ListHelp.intersection(g,f);
	    if(g.length==1) {
		list[count]=f[i];
		++count;
	    }
	}
	return list;
    }
    

    


    /**This finds the adjecent triangles with the desired tags.
       In this case we are looking for 2 such triangles*/
    
    public static int[] findAdjacentTriangles(TriangleFiller X,int[] k) {
	for(int i=0;i<X.count;++i) {
	    for(int j=0;j<X.count;++j) {
		if((X.TAG[i]==k[0])&&(X.TAG[j]==k[1])) {
		    if(geometricallyAdjacent(X,i,j)==true) {
			int[] A={i,j};
			return A;
		    }
		}
	    }
	}
	return null;
    }

    public static int[] findVertexSharingTriangles(TriangleFiller X,int[] k) {
	for(int i=0;i<X.count;++i) {
	    for(int j=0;j<X.count;++j) {
		if((X.TAG[i]==k[0])&&(X.TAG[j]==k[1])) {
		    if(geometricallyVertexSharing(X,i,j)==true) {
			int[] A={i,j};
			return A;
		    }
		}
	    }
	}
	return null;
    }





    public static boolean geometricallyAdjacent(TriangleFiller X,int i,int j) {
        Complex z0=X.triangleCenter(i);
	Complex z1=X.triangleCenter(j);
	double d=Complex.dist(z0,z1);
	d=d-Math.sqrt(3)/3;
	if(Math.abs(d)<.0000001) return true;
	return false;
    }

    public static boolean geometricallyVertexSharing(TriangleFiller X,int i,int j) {
        Complex z0=X.triangleCenter(i);
	Complex z1=X.triangleCenter(j);
	double d=Complex.dist(z0,z1);
	if(Math.abs(d-1)<.0000001) return true;
	return false;
    }

    
    /**this handles the triangle case*/
    public static PolygonWrapper fit1(TriangleFiller X,EnhancedPolygon E,int[][] piece) {
	int t=piece[0][0];
	for(int i=0;i<X.count;++i) {
	    if(X.TAG[i]==t) {
		return new PolygonWrapper(3,X.TRI[i]);
	    }
	}
	return null;
    }

    
    /**This takes adjacent triangles and returns the vertex of
       the first one that is not contained in the second one*/
    
    public static int outIndex(Complex[] Z1,Complex[] Z2) {
	for(int i=0;i<3;++i) {
	    boolean test=false;
	    for(int j=0;j<3;++j) {
		double d=Complex.dist(Z1[i],Z2[j]);
		if(d<.0000001) test=true;
	    }
  	    if(test==false) return i;
	}
	return -1;
    }

    /**This finds the index on the first triangle which names
       the vertex also in the second triangle*/

    public static int matchIndex(Complex[] Z1,Complex[] Z2) {
	for(int i=0;i<3;++i) {
	    for(int j=0;j<3;++j) {
		double d=Complex.dist(Z1[i],Z2[j]);
		if(d<.0000001) return i;
	    }
	}
	return -1;
    }
    
    /**This finds the index on the first triangle which names
       the vertex that is 2 units away from a vertex on the second triangle.*/
    

    public static int farIndex(Complex[] Z1,Complex[] Z2) {
	for(int i=0;i<3;++i) {
	    for(int j=0;j<3;++j) {
		double d=Complex.dist(Z1[i],Z2[j]);
		d=Math.abs(d-2);
		if(d<.0000001) return i;
	    }
	}
	return -1;
    }


    
    

    /**gets the first index on the boundary where the number of incident triangles is k*/
    public static int firstIndex(int[][] piece,int k) {
        int index=-1;
	int[] G=piece[2];
     	for(int i=0;i<G.length;++i) {
	    if(G[i]==k) index=i;
	}
	return index;
    }

    /**This gets the vertex adjacent to k which is incident to the given triangle*/

    public static boolean isCorrectlyPointed(EnhancedPolygon E,int[][] piece,int index,int tri) {
	int[] G=piece[2];
	int index1=(index+1)%G.length;
	int[] g1=E.flower[piece[1][index1]];
	if(ListHelp.onList(tri,g1)==false) return false;
	return false;
    }



    
}


