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


public class PictureCanvas extends ScaleCanvas implements MouseListener, MouseMotionListener {
    Manager M;
    PolygonWrapper[] TRI=new PolygonWrapper[4000];
    PolygonWrapper[] QUAD=new PolygonWrapper[2];
    int[][] VERTEX=new int[1000][2];
    int[] FACE=new int[1000];
    int[] SOL=new int[1000];
    int[][] FLOWER=new int[1000][6];
    Solver SOLV;
    int VCOUNT=0;
    int FCOUNT=0;
    int COUNT=0;
    Color COLOR;
    int[] VTX=new int[2];
    int[] INIT=new int[4];

     public PictureCanvas() {
	 addMouseListener(this);
	 addMouseMotionListener(this);
	 setScales(500,900,100);
	 makeTriangles();
	 computeVertices();
	 SOLV=new Solver();
	 computeFlowers();
	 VTX[0]=0;
	 VTX[1]=0;
     }


    public void makeTriangles() {
	COUNT=0;
	int count=0;
	for(int i=-20;i<20;++i) {
	    for(int j=0;j<20;++j) {
		TRI[count]=PolygonWrapper.gridTriangle(0,i,j);
		TRI[count].index=count;
		++count;
		TRI[count]=PolygonWrapper.gridTriangle(1,i,j);
		TRI[count].index=count;
		++count;
	    }
	}
	COUNT=count;
	QUAD[0]=new PolygonWrapper();
	QUAD[0].count=4;
	QUAD[0].z[0]=Complex.eis(0,0);
	QUAD[0].z[1]=Complex.eis(1,2);
	QUAD[0].z[2]=Complex.eis(-2,4);
	QUAD[0].z[3]=Complex.eis(-3,2);
	QUAD[1]=new PolygonWrapper();
	QUAD[1].count=4;
	for(int i=0;i<4;++i) QUAD[1].z[i]=Complex.plus(QUAD[0].z[i],QUAD[0].z[1]);
    }

    public void computeVertices() {
	VCOUNT=0;
	int count=0;
	for(int i=-20;i<20;++i) {
	    for(int j=0;j<20;++j) {
		Complex w=Complex.eis(i,j);
		boolean test=isQuadVertex(w);
		if(test==true) {
		    VERTEX[count][0]=i;
		    VERTEX[count][1]=j;
		    ++count;
		}
	    }
	}
	VCOUNT=count;
    }



    /***this computes the flowers**/
    public void computeFlowers() {
	int[] face=new int[1000];
	int count=0;
	for(int i=0;i<VCOUNT;++i) {
	    FLOWER[i]=getFlower(VERTEX[i][0],VERTEX[i][1]);
	    if(FLOWER[i].length==6) FLOWER[i]=reorderList(FLOWER[i]);
	    
	    for(int j=0;j<FLOWER[i].length;++j) {
   		face[count]=FLOWER[i][j];
		++count;
	    }
	}
	FACE=ListHelp.irredundantNonzero(face);

	for(int i=0;i<VCOUNT;++i) {
	    for(int j=0;j<FLOWER[i].length;++j) {
		int k=ListHelp.lookup(FLOWER[i][j],FACE);
		FLOWER[i][j]=k;
	    }
	}
	
	SOLV=new Solver(FLOWER,VCOUNT);

	SOLV.print(false);
    }



    

    /**This finds all the flowers adjacent to a given
       vertex, but in an unordered way.*/

    
    public  int[] getFlower(int a,int b) {
	Complex z=Complex.eis(a,b);
	int[] list=new int[7];
	int count=0;
	
	for(int i=0;i<COUNT;++i) {
	    Complex c=TRI[i].center();
	    boolean test=isQuadVertex(c);
	    if(test==true) {
		boolean test1=false;
		for(int q=0;q<5;++q) {
		    Complex zz=basicMap(q,z);
	            boolean test2=TRI[i].isVertex(zz);
		    if(test2==true) test1=true;
		}
	        if(test1==true) {
		   list[count]=i;
		   ++count;
		}
	    }
	}
	int[] list2=new int[count];
	for(int i=0;i<count;++i) list2[i]=list[i];
	return list2;
    }

    public int[] reorderList(int[] LIST0) {
	int[] LIST1=new int[LIST0.length];
	int i0=-1;
	int i1=LIST0[0];
	int count=1;
	LIST1[0]=i1;

	for(int i=1;i<LIST0.length;++i) {
	    int i2=getNext(i0,i1,LIST0);
	    LIST1[count]=i2;
	    i0=i1;
	    i1=i2;
	    ++count;
	}
	return LIST1;
    }
    

    public int getNext(int i0,int i1,int[] LIST) {
	for(int i=0;i<LIST.length;++i) {
	    double test=orbifoldDistance(i1,LIST[i]);
	    if((test>.5)&&(test<.6)&&(LIST[i]!=i0)) return LIST[i];
	}
	return -1;
    }


    public double orbifoldDistance(int i0,int i1) {
   	Complex c0=TRI[i0].center();
	Complex c1=TRI[i1].center();
	double dist=100000;
	for(int q=0;q<5;++q) {
	   Complex c2=basicMap(q,c1);
           double test=Complex.dist(c0,c2);
	   if(test<dist) dist=test;
	}
	return dist;
    }

    public Complex basicMap(int q,Complex z) {
	if(q==0) return z;
	if(q==1) return Complex.plus(z,QUAD[0].z[1].scale(2));
	if(q==2) return Complex.minus(z,QUAD[0].z[1].scale(2));
    	if(q==3) return Complex.minus(QUAD[0].z[2].scale(2),z);
    	if(q==4) return Complex.minus(QUAD[0].z[1].scale(2),z);
	return z;
    }
    /****done computing flowers**/

    


    

   public void paint(Graphics g2) {
      Graphics2D g=(Graphics2D) g2;
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
      drawBG(g);
      drawTriangles(g);
      drawGrid(g);
      drawControlPoints(g);
      drawVertex(g);
      drawDebug(g);
   }



    public void drawDebug(Graphics2D g) {
	int m=M.C.DEBUG.mode;
	if(m==1) drawDebugFlowers(g);
	if(m==2) drawFaceNeighbors(g);

    }
        
    public void drawDebugFlowers(Graphics2D g) {
	Complex z=Complex.eis(VTX[0],VTX[1]);
	int[] f=getFlower(VTX[0],VTX[1]);
	f=reorderList(f);
	ListHelp.print(f);

	Color[] C=new Color[6];
	for(int i=0;i<6;++i) C[i]=new Color(130+20*i,0,130+20*i);
	
	for(int i=0;i<f.length;++i) {
	    Path2D.Double p=TRI[f[i]].toPath();
	    p=transform(p);
	    g.setColor(C[i]);
	    g.fill(p);
	}

	fillPoint(g,z,.15,Color.green,32);
    }

    public void drawFaceNeighbors(Graphics2D g) {
	int index=-1;
	Path2D.Double p=new Path2D.Double();
	for(int i=0;i<FACE.length;++i) {
	    p=TRI[FACE[i]].toPath();
	    if(p.contains(SOURCE.x,SOURCE.y)==true) {
		index=i;
	    }
	}
	if(index==-1) return;
	Color C2=new Color(255,200,0);
	int[] list=SolverAnalyze.faceNeighborsSolid(SOLV,index);


	for(int q=0;q<27;++q) {
	  for(int i=0;i<list.length;++i) {
	    index=list[i];
	    PolygonWrapper K=new PolygonWrapper(TRI[FACE[index]]);
	    K=QuadMap.doMap(QUAD[0],q,K);
	    p=K.toPath();
	    p=transform(p);
	    g.setColor(C2);
	    g.fill(p);
	    g.setColor(Color.black);
	    g.draw(p);
	  }
	}

    }
    


    
    public void drawBG(Graphics2D g) {
	g.setColor(M.C.DISPLAY.M[0].C);
        g.fillRect(0,0,getWidth(),getHeight());
    }
    
    public void drawTriangles(Graphics2D g) {
	Path2D.Double p=new Path2D.Double();
	Color[] C={M.C.DISPLAY.M[1].C,M.C.DISPLAY.M[2].C,M.C.DISPLAY.M[3].C};

	for(int i=0;i<FACE.length;++i) {
	    for(int q=0;q<27;++q) {
	         PolygonWrapper K=new PolygonWrapper(TRI[FACE[i]]);
		 K=QuadMap.doMap(QUAD[0],q,K);
                 p=K.toPath();
	         p=transform(p);
	         g.setColor(C[SOL[i]]);
	         g.fill(p);
	         g.setColor(Color.black);
	         g.draw(p);
	    }
	}
    }
    


    public void drawVertex(Graphics2D g) {
	if(M.C.DISPLAY.L[6].on==0) return;
	for(int i=0;i<VCOUNT;++i) {
	    Complex z=Complex.eis(VERTEX[i][0],VERTEX[i][1]);
	    fillPoint(g,z,.1,M.C.DISPLAY.M[6].C,32);
	}
    }


    /**This draws the parallelogram grid*/
    
    public void drawGrid(Graphics2D g) {
	if(M.C.DISPLAY.L[4].on==0) return;
	Path2D.Double p=new Path2D.Double();
	  for(int i=0;i<2;++i) {
	    for(int q=0;q<27;++q) {
     		PolygonWrapper K=QuadMap.doMap(QUAD[0],q,QUAD[i]);
	         p=K.toPath();
	         g.setStroke(new BasicStroke(2));
	         p=transform(p);
	         g.setColor(M.C.DISPLAY.M[4].C);
	         g.draw(p);
	         g.setStroke(new BasicStroke(1));
	    }
	  }
    }

    public void drawControlPoints(Graphics2D g) {
	if(M.C.DISPLAY.L[5].on==0) return;
	
  	  double[] d={.23,.3,.23,.3};
	  Color[] C2={M.C.DISPLAY.M[2].C,M.C.DISPLAY.M[3].C};
	  if(M.C.ACTION.L[0].on==0) {
	    C2[0]=M.C.DISPLAY.M[1].C;
	    C2[1]=M.C.DISPLAY.M[1].C;
	  }
	
	  for(int i=0;i<4;++i) {
	     fillPoint(g,QUAD[0].z[i],d[i],Color.black,6);
	     fillPoint(g,QUAD[0].z[i],.2,C2[INIT[i]],6);
     	  }
	  if(M.C.DISPLAY.L[7].on==1) fillPoint(g,SOURCE,.06,M.C.DISPLAY.M[7].C,32);
    }





    

    public boolean testTriangle(int i) {
	Complex w=TRI[i].center();
	return isQuadVertex(w);
    }

    public boolean isQuadVertex(Complex w) {
	for(int i=0;i<4;++i) {
	    if(Complex.dist(w,QUAD[0].z[i])<.0001) {
		return true;
	    }
	}
	
	PolygonWrapper Q0=QUAD[0].contract(-.0001);
	PolygonWrapper Q1=QUAD[1].contract(+.0001);
	Path2D.Double p0=Q0.toPath();
	Path2D.Double p1=Q1.toPath();
	if(p0.contains(w.x,w.y)) return true;
	if(p1.contains(w.x,w.y)) return true;
	return false;
    }





    

    public void setQuad() {

	int index=-1;
	for(int i=0;i<4;++i) {
	    if(Complex.dist(SOURCE,QUAD[0].z[i])<.2) index=i;
	}
	if(index!=-1) {
	    INIT[index]=1-INIT[index];
	    return;
	}

	if(SOURCE.y<0) return;
	int x0=0;
	int y0=0;
	double min=100000;
	for(int i=-20;i<20;++i) {
	    for(int j=-20;j<20;++j) {
		double d=Complex.dist(SOURCE,Complex.eis(i,j));
		if(d<min) {
		    min=d;
		    x0=i;
		    y0=j;
		}
	    }
	}
	Complex w=Complex.eis(x0,y0);
	double d1=Complex.dist(w,QUAD[0].z[1]);
	double d3=Complex.dist(w,QUAD[0].z[3]);
	if(d1<d3) QUAD[0].z[1]=new Complex(w);
	else QUAD[0].z[3]=new Complex(w);
	QUAD[0].z[2]=Complex.plus(QUAD[0].z[1],QUAD[0].z[3]);
	
	for(int i=0;i<4;++i) QUAD[1].z[i]=Complex.plus(QUAD[0].z[i],QUAD[0].z[1]);
	computeVertices();
	computeFlowers();
    }

    

    /**This routine finds the nearest vertex*/
    
    public void setVertex() {
	if(SOURCE.y<0) return;
	int x0=0;
	int y0=0;
	double min=100000;
	for(int i=-20;i<20;++i) {
	    for(int j=-20;j<20;++j) {
		double d=Complex.dist(SOURCE,Complex.eis(i,j));
		if(d<min) {
		    min=d;
		    x0=i;
		    y0=j;
		}
	    }
	}
	Complex z=Complex.eis(x0,y0);
	boolean test=isQuadVertex(z);
	if(test==true) {
	   VTX[0]=x0;
	   VTX[1]=y0;
	  
	}
    }

    

    

    
    public void mousePressed(MouseEvent e) { }
    
    public void mouseClicked(MouseEvent e) { 
	MouseData J=MouseData.process(e);
        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(M.C.DEBUG.mode==0) setQuad();
            if(M.C.DEBUG.mode==1) setVertex();
	}
	repaint();
    }

    public void mouseDragged(MouseEvent e) { 
	MouseData J=MouseData.process(e);
	if(J.mode==2) {
	    SOURCE=unTransform(J.X);
            if(M.C.DEBUG.mode==0) setQuad();
            if(M.C.DEBUG.mode==1) setVertex();
	}
	repaint();
    }

    
     public void mouseReleased(MouseEvent e) {	 
     }

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

     public void mouseMoved(MouseEvent e) {}   



}

