import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.math.*;


public class PictureCanvas extends ScaleCanvas implements MouseListener, MouseMotionListener {
    Manager M;
    ListenSquare CONTROL,AUTOFIT;
    ControlPanel ACTION;
    int TRIANGLE;
    int INDEX;
    ListenSquare EXPORT;
    Output OUT;

     public PictureCanvas() {
	 addMouseListener(this);
	 addMouseMotionListener(this);
	 setScales(250,435,200);
	 CONTROL=new ListenSquare(0,0,55,95);
	 setPanels();
	 INDEX=0;
	 EXPORT=new ListenSquare(0,55,55,20);
	 AUTOFIT=new ListenSquare(0,75,55,20);
     }
    

    
    public void setPanels() {

       Color[] C0={new Color(100,150,255),
                   Color.white,
                   Color.white,
                   Color.black,
                   Color.white};

       String[] ActionString={"pole","puzzle","action"};
       int[] ActionState={0,1};
        ACTION=new ControlPanel(C0,ActionString,ActionState,2);
	ACTION.mode=1;
    }


   public void paint(Graphics g2) {
      Graphics2D g=(Graphics2D) g2;
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
      try{scaleNicely();}
      catch(Exception e) {}
      drawBG(g);
      drawTriangulation(g);
      drawPuzzle(g);
      drawPuzzleOutline(g);
      drawSpecialVertices(g);
      drawControls(g);
   }


    /**This scales nicely*/
    public boolean scaleNicely() {
	if(AUTOFIT.on==0) return false;
	Complex[] Z={new Complex(1,0),new Complex(0,1),new Complex(-1,0),new Complex(0,-1)};
	PolygonWrapper P=new PolygonWrapper(4,Z);
	Path2D.Double GP=P.toPath();
   	AffineTransform[] AA=preFit(GP,20,20,getWidth()-40,getHeight()-40);
	A[0]=AA[0];
	A[1]=AA[1];
	return(true);
    }

    public static AffineTransform[] preFit(Path2D.Double gp,double x,double y,double w,double h) {
	AffineTransform[] AFF=new AffineTransform[2];
	Rectangle2D R0=gp.getBounds2D();
	Rectangle2D.Double R1=(Rectangle2D.Double)(R0);
	double[] t={R1.x,R1.y,R1.x+R1.getWidth(),R1.y+R1.getHeight()};
	 double x1=1.0*(w)/(t[2]-t[0]);
	 double x2=1.0*(h)/(t[3]-t[1]);
	 double sc=x1;
	 if(sc>x2) sc=x2;	 
         AFF[1]=AffineTransform.getScaleInstance(sc,sc);
	 double cenx=(t[0]+t[2])/2.0;
	 double ceny=(t[1]+t[3])/2.0;
	 double targetx=x+1.0*w/2;
	 double targety=y+1.0*h/2;
	 double xx=targetx-cenx*sc;
	 double yy=targety-ceny*sc;
	 AFF[0]=AffineTransform.getTranslateInstance(xx,yy);
	 return(AFF);
    }

    /**done scaling*/



    /**This gets the monochrome pieces*/
    
    public void drawPuzzle(Graphics2D g) {
	try{
	  if(M.S.DISPLAY.L[10].on==0) return;
	}
	catch(Exception e) {return;}
	if(M.S.E.SOLVED==false) return;
        Color C1=M.S.DISPLAY.M[10].C;
	
	int[][] m=ShapeRecognizer.monochromePiece(M.S.E,TRIANGLE);
	for(int i=0;i<m[0].length;++i) drawTriangle(g,M.S.E,m[0][i],C1,C1); //draws the triangles individually

	PolygonWrapper P=getMonochromePiece(TRIANGLE);
	if(P==null) return;
    }
    
    public void drawPuzzleOutline(Graphics2D g) {
	try{
	  if(M.S.DISPLAY.L[11].on==0) return;
	}
	catch(Exception e) {return;}
	if(M.S.E.SOLVED==false) return;
        Color C2=M.S.DISPLAY.M[11].C;

	PolygonWrapper P=getMonochromePiece(TRIANGLE);
	if(P==null) return;
	Path2D.Double p=P.toPath();
	p=transform(p);
	g.setStroke(new BasicStroke(4));
	g.setColor(Color.black);
	g.draw(p);
	g.setStroke(new BasicStroke(2));
	g.setColor(Color.white);
	g.draw(p);
	g.setStroke(new BasicStroke(1));
    }

    public PolygonWrapper getMonochromePiece(int a) {
    	int[][] n0=ShapeRecognizer.monochromePiece(M.S.E,a);
	if(n0==null) return null;
	int[] n=n0[1];
  	Complex[] Z=new Complex[n.length];
	for(int i=0;i<n.length;++i) {
	    if(n[i]==-1) return null;
  	    Z[i]=M.S.E.embed[n[i]];
	    if(n[i]==M.S.E.INFINITE) return null;
	}
	PolygonWrapper P=new PolygonWrapper(n.length,Z);
	return P;
    }



    
    
    public void drawBG(Graphics2D g) {
	Color C=Color.black;
        try{
	    if(M.S.E.PERFECT==true) C=M.S.DISPLAY.M[2].C;
	}
	catch(Exception e) {}
	g.setColor(C);
        g.fillRect(0,0,getWidth(),getHeight()); 
    }

    public void drawControls(Graphics2D g) {
	CONTROL.render(g,new Color(0,0,100));
	ACTION.render(g,0,0,55);
        EXPORT.render(g,"export",12,2,new Color(80,100,120));
        if(AUTOFIT.on==1) AUTOFIT.render(g,"fit: yes",12,2,new Color(200,0,200));
        if(AUTOFIT.on==0) AUTOFIT.render(g,"fit: no",12,2,new Color(0,0,0));

		       
    }

    public void drawTriangulation(Graphics2D g) {
	int n=0;
	Color[] COL={new Color(180,180,180),new Color(50,100,255,150),Color.white,Color.black,Color.red};
	
	try{
            n=M.S.E.solution.length;
	    int[] t={2,1,6,7,8};
	    for(int i=0;i<5;++i) COL[i]=M.S.DISPLAY.M[t[i]].C;
	}
	catch(Exception e) {
	    System.out.println("bad");
	}

	boolean obvious=false; 
	for(int i=0;i<n;++i) {
	    int j=M.S.E.solution[i];
	    if(j!=-1) drawTriangle(g,M.S.E,i,COL[2+j],COL[4]);
	    if(j==-1) {
		drawTriangle(g,M.S.E,i,COL[0],COL[1]);
		obvious=true;
	    }
	}

	try{
	    if((obvious==false)&&(M.S.E.SOLVED==false)) {
	    g.setFont(new Font("Helvetica",Font.PLAIN,18));
	    g.setColor(Color.red);
	    g.drawString("not a solution",10,222);
	    }
	}
	catch(Exception e) {}
    }


    


    public void drawTriangle(Graphics2D g,EnhancedPolygon E,int k,Color C1,Color C2) {
	PolygonWrapper X=getTriangle(E,k);
	Path2D.Double p=X.toPath();
	p=transform(p);
	g.setColor(C1);
	g.fill(p);
	g.setColor(C2);
	g.draw(p);
	
  }

    
    public PolygonWrapper getTriangle(EnhancedPolygon E,int k) {
       	int[] q=E.triangles[k];
	boolean test=false;
	for(int i=0;i<3;++i) {
	    if(q[i]==E.INFINITE) test=true;
	}
	if(test==true) return getTriangleInfinity(E,k);
	return getTriangleFinite(E,k);
  }

    public PolygonWrapper getTriangleInfinity(EnhancedPolygon E,int k) {
	int[] q=E.triangles[k];
	Complex[] z=new Complex[4];
	int count=0;
	for(int i=0;i<3;++i) {
	    if(E.embed[q[i]].norm()>.00000000001) {
		z[count]=new Complex(E.embed[q[i]]);
		++count;
	    }
	}
	z[2]=z[1].scale(10);
	z[3]=z[0].scale(10);
	PolygonWrapper X=new PolygonWrapper(4,z);
	return X;
    }

    public PolygonWrapper getTriangleFinite(EnhancedPolygon E,int k) {
	int[] q=E.triangles[k];
	Complex[] z=new Complex[3];
	for(int i=0;i<3;++i) {
	    z[i]=E.embed[q[i]];
	    if(z[i]==null) return null;
	}
	PolygonWrapper X=new PolygonWrapper(3,z);
	return X;
    }

    public void drawSpecialVertices(Graphics2D g) {
	try{
   	  if(M.S.DISPLAY.L[3].on==0) return;
	}
	catch(Exception e) {return;}
	Color C=M.S.DISPLAY.M[3].C;
	EnhancedPolygon E=new EnhancedPolygon();
	try{E=M.S.E;}
	catch(Exception e) {return;}
	for(int i=0;i<E.graph.length;++i) {
	  if(i!=E.INFINITE) {
	      if(E.graph[i].length<6) {
	         double d=GraphEmbed.linkRadius(E,i);
                 Complex z=E.embed[i];
	         fillPoint(g,z,d/6,Color.black,32);
	         fillPoint(g,z,d/8,C,32);
	      }
	  }
	}
    }


    public void switchInfinity() {
	int index=recognizeVertex();
	M.S.E.INFINITE=index;
	GraphEmbed.setGeometry(M.S.E);
    }
    
    public void getTriangle() {
	int index=recognizeTriangle();
	if(index!=-1) TRIANGLE=index;
    }



    /**recognition routines: one for triangles one for vertices*/
    
    public int recognizeTriangle() {
	for(int k=0;k<M.S.E.triangles.length;++k) {
	    PolygonWrapper P=getTriangle(M.S.E,k);
	    Path2D.Double p=P.toPath();
	    if(p.contains(SOURCE.x,SOURCE.y)==true) {
		M.S.FUDGE_VAL=1-M.S.FUDGE_VAL;
		return k;
	    }
	}
	return -1;
    }

    public int recognizeVertex() {
	double min=1000;
	int index=0;
	for(int i=0;i<M.S.E.embed.length;++i) {
	    double test=Complex.dist(SOURCE,M.S.E.embed[i]);
	    if(test<min) {
		min=test;
		index=i;
	    }
	}
	return index;
    }



    /**export routines*/
    public void doExport() {
	OUT=new Output("Output/graph");
	exportTriangulation();
    }

    public void exportTriangulation() {
        int n=M.S.E.solution.length;
	Color[] COL=new Color[5];
         int[] t={2,1,6,7,8};
	 for(int i=0;i<5;++i) COL[i]=M.S.DISPLAY.M[t[i]].C;
	
	for(int i=0;i<n;++i) {
	    int j=M.S.E.solution[i];
	    if(j!=-1) exportTriangle(M.S.E,i,COL[2+j],COL[4],1);
	    if(j==-1) exportTriangle(M.S.E,i,COL[0],COL[1],1);
	}
    }


    public void exportTriangle(EnhancedPolygon E,int k,Color C1,Color C2,double w) {
	PolygonWrapper X=getTriangle(E,k);
	Path2D.Double p=X.toPath();
	OUT.polyWrite(p,C1,C2,w);
    }
    /**end export routines*/


    

    public void mousePressed(MouseEvent e) { }
    
    public void mouseClicked(MouseEvent e) { 
	MouseData J=MouseData.process(e);
	if(CONTROL.inside(J.X)==1) {
	    if(EXPORT.inside(J.X)==1) doExport();
	    ACTION.switchMode(J.X);
	    if(AUTOFIT.inside(J.X)==1) AUTOFIT.on=1-AUTOFIT.on;
	    M.repaint();
	    return;
	}
        if(J.mode==1)  scaleUp(J.X,0);
        if(J.mode==3)  scaleUp(J.X,1);
	if(J.mode==2) {
	    SOURCE=unTransform(J.X);
	    if(ACTION.mode==0) switchInfinity();
	    if(ACTION.mode==1) getTriangle();
	}
	M.repaint();
    }

    public void mouseDragged(MouseEvent e) { 
	MouseData J=MouseData.process(e);
	if(J.mode==2) {
	    SOURCE=unTransform(J.X);
	}
	M.repaint();
    }

    
     public void mouseReleased(MouseEvent e) {	 
     }

     public void mouseEntered(MouseEvent e) {}
     public void mouseExited(MouseEvent e) {}   

     public void mouseMoved(MouseEvent e) {}   



}

