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


public class DeformCanvas extends ScaleCanvas implements MouseListener, MouseMotionListener {
    Manager M;
    PuzzlePiece[] P=new PuzzlePiece[100];
    ListenSquare GO,STOP,CONTROL,AUTOFIT,RESET;
    SelectInteger LIMIT,TOL;
    ControlPanelColor DISPLAY;
    Deformer DEFORM;
    double[][] VAR=new double[10][100];
    int VCOUNT;
    int COUNT;
    String[] STR=new String[5];
    int[][] INDEX=new int[2][3];
    SelectInteger[] MOVE=new SelectInteger[10];
    SelectInteger MOVE0;
    boolean READY;


     public DeformCanvas() {
	 addMouseListener(this);
   	 addMouseMotionListener(this);
	 CONTROL=new ListenSquare(0,0,120,0);
	 setScales(300,400,40,40);
	 GO=new ListenSquare(0,0,45,20);
	 STOP=new ListenSquare(45,0,40,20);
	 AUTOFIT=new ListenSquare(0,130,45,20);
	 LIMIT=new SelectInteger(5,30,40,20,4,1,10,1);
	 TOL=new SelectInteger(5,80,40,20,30,10,50,1);
	 DEFORM=new Deformer();
	 setDeformControls();
	 COUNT=0;
	 VCOUNT=4;
	 for(int i=0;i<5;++i) STR[i]="";
	 setPanels();
	 READY=false;
     }

      public void setDeformControls() {
	 RESET=new ListenSquare(0,265,40,20);
	 for(int i=0;i<10;++i) MOVE[i]=new SelectInteger(5,300+25*i,40,20,0,-20,20,1);
	 MOVE0=new SelectInteger(50,265,40,20,4,1,10,1);
      }

	 


    public void setPanels() {

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

	String[] DisplayString={"background",
				"pieces",
				"gluings",
                                "display"};
	Color[] DisplayColor={new Color(180,180,180),
			      new Color(0,0,255),
                              new Color(255,0,0),
   			      new Color(50,100,255)};
	int[] DisplayState={1,1,1,1};
        DISPLAY=new ControlPanelColor(C0,DisplayString,DisplayState,3,DisplayColor);

    }
    
    
    /**drawing routines*/
   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);
      drawPieces(g);
      try{drawGluings(g);} catch(Exception e) {}
      drawControls(g);
      drawMessage(g);
    }

    /**This scales nicely*/
    public boolean scaleNicely() {
  	if(AUTOFIT.on==0) return(false);
	if(COUNT==0) return false;
	PolygonWrapper Y=getBoundingBox();
	Path2D.Double GP=Y.toPath();
   	AffineTransform[] AA=preFit(GP,150,40,getWidth()-180,getHeight()-80);
	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);
    }
    
    public PolygonWrapper getBoundingBox() {
	Puzzler.guidedAssembly(P,COUNT,M.X.RECORD);
	double minx=10000;
	double miny=10000;
	double maxx=-10000;
	double maxy=-10000;
	for(int i=0;i<COUNT;++i) {
  	    PolygonWrapper Q=P[i].getTranslate();
	    for(int j=0;j<Q.count;++j) {
		Complex w=Q.z[j];
		if(minx>w.x) minx=w.x;
		if(maxx<w.x) maxx=w.x;
		if(miny>w.y) miny=w.y;
		if(maxy<w.y) maxy=w.y;
	    }
	}
	Complex[] Z={new Complex(minx,miny),new Complex(minx,maxy),new Complex(maxx,maxy),new Complex(maxx,miny)};
	PolygonWrapper Y=new PolygonWrapper(4,Z);
	return Y;
    }
    /**end scaling routines*/
    
    
    public void drawBG(Graphics2D g) {
 	g.setColor(DISPLAY.M[0].C);
        g.fillRect(0,0,getWidth(),getHeight()); 
    }

    public void drawMessage(Graphics2D g) {
	g.setColor(Color.white);
        g.setFont(new Font("Helvetica",Font.PLAIN,14));
	for(int i=0;i<5;++i) {
	    g.drawString(STR[i],10,230+16*i);
	}
    }


    

    public void drawPieces(Graphics2D g) {
   	Color[] COL={M.S.DISPLAY.M[6].C,M.S.DISPLAY.M[7].C};
	Color COL2=M.S.DISPLAY.M[8].C;
	if(COUNT==0) return;
	Puzzler.guidedAssembly(P,COUNT,M.X.RECORD);
 	for(int i=0;i<COUNT;++i) P[i].render(this,g,COL,COL2);
    }

    public void drawGluings(Graphics2D g) {
	for(int q=0;q<2;++q) {
  	    int i=INDEX[q][0];
	    if(i==-1) return;
   	    P[i].render(this,g,INDEX[q],DISPLAY.M[2].C);
	}
    }





    

    
    public void drawControls(Graphics2D g) {
	CONTROL.h=this.getHeight();
	CONTROL.render(g,new Color(0,0,100));
	DISPLAY.render(g,0,150,120);
	LIMIT.render(g,new Color(0,150,0),Color.white,Color.white);
	TOL.render(g,new Color(0,150,0),Color.white,Color.white);
	g.setColor(Color.white);
        g.setFont(new Font("Helvetica",Font.PLAIN,14));
	g.drawString("# vars",5,LIMIT.y+40);
	g.drawString("tolerance",5,TOL.y+40);

	try {drawDeformationControls(g);}
	catch(Exception e) {}
    
   	if(DEFORM.halt==false) STOP.render(g,"stop",12,4,Color.red);
	if(DEFORM.halt==true) GO.render(g,"deform",12,4,new Color(0,200,0));
	if(AUTOFIT.on==1)   AUTOFIT.render(g,"fit: yes",12,4,new Color(200,0,200));
        if(AUTOFIT.on==0)   AUTOFIT.render(g,"fit: no",12,4,new Color(200,0,200));
    }


    public void drawDeformationControls(Graphics2D g) {
	for(int i=0;i<LIMIT.val;++i) MOVE[i].render(g,Color.red,Color.white,new Color(0,0,0,0));
	MOVE0.render(g,new Color(200,0,0),Color.white,Color.white);
   	RESET.render(g,"reset",12,4,new Color(200,0,0));
    }

    

    public void doControls(Point X) {
	if(GO.inside(X)==1) {
	    DEFORM=new Deformer(M.X,this);
	   new Thread(DEFORM).start();
	}
	if(STOP.inside(X)==1) {
	    DEFORM.halt=true;
	}
	LIMIT.modify(X);
	TOL.modify(X);

       if(RESET.inside(X)==1) {
	    for(int i=0;i<M.X.COUNT;++i) P[i]=new PuzzlePiece(M.X.P[i]);
	    Puzzler.guidedAssembly(P,M.X.COUNT,M.X.RECORD);
	    for(int i=0;i<LIMIT.val;++i) MOVE[i].val=0;
	}

	if(READY==false) return;
        MOVE0.modify(X);
	for(int i=0;i<LIMIT.val;++i) {
	    MOVE[i].modify(X);
	    int test=MOVE[i].upOrDown(X);
	    double d=1.0/Math.pow(2,MOVE0.val);
	    d=d*test;
	    double[] var=Deformer.copy(VAR[i]);
	    for(int ii=0;ii<var.length;++ii) var[ii]=d*var[ii];
     	    P=PuzzleGeometry.deformation(P,COUNT,var);
	}
	    
    }


    /**getting the gluing maps*/
    public void resetIndex() {
	for(int i=0;i<2;++i) {
	    for(int j=0;j<3;++j) {
		INDEX[i][j]=-1;
	    }
	}
    }

    public void recognize() {
	resetIndex();
	for(int i=0;i<COUNT;++i) {
 	    if(P[i].engulfs(SOURCE)==true) {
		INDEX[0][0]=i;
	    }
	}
    }

    public void getGlue() {
	recognize();
	if(INDEX[0][0]==-1) return;
	int[] test=P[INDEX[0][0]].recognizeSide(SOURCE);
	if(test==null) return;
  	INDEX[0][1]=test[0];
	INDEX[0][2]=test[1];

	for(int i=0;i<COUNT;++i) {
	    boolean match=P[i].hasSide(test);
  	    if((match==true)&&(i!=INDEX[0][0])) {
		INDEX[1][0]=i;
	        INDEX[1][1]=test[0];
	        INDEX[1][2]=test[1];
	    }
	}
    }
    /**end gluing routines*/
    

    
    public void mouseClicked(MouseEvent e) {
        MouseData J=MouseData.process(e);
	if(CONTROL.inside(J.X)==1) {
  	    doControls(J.X);
	    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);
	}
	repaint();
    }

    public void mousePressed(MouseEvent e) {
    }


     public void mouseReleased(MouseEvent e) {
     }

     public void mouseEntered(MouseEvent e) {
	 requestFocus();
     }
    
     public void mouseExited(MouseEvent e) {}   
    
    public void mouseMoved(MouseEvent e) {
	MouseData J=MouseData.process(e);
	SOURCE=unTransform(J.X);
	recognize();
	getGlue();
	repaint();
    }

    public void mouseDragged(MouseEvent e) {
	MouseData J=MouseData.process(e);
    }


}

    
