import java.awt.*;
import java.applet.*;
import java.util.*;
import java.lang.*;

public class CG_hw_08 extends PixApplet
{

	int pArr=0, qArr=0;
	Graphics G;
	
	
	double array[][][]=new double[20][20][6];
	double crossP[][][] = new double [20][20][6];
	double surfaces[][][] = new double [20][20][6];
	
	int tempInd=0;
	
	double V[][][]= new double[20][20][6];
	double polygon[][][]= new double[800][3][6];
	
	ParametricSphere2 sphere = new ParametricSphere2(0,0,0,.2);
	ParametricSphere2 sphere2 = new ParametricSphere2(0,0,0,.4);
	
	double matrix[][]=new double[4][4];
	
	double uArray[][][] = new double[20][20][6];
	double vArray[][][] = new double[20][20][6];
	
	
	double theta=Math.PI;
	static int Xarr[]=new int [4];
	static int Yarr[]=new int [4];
	
	int temp1[]={250,250,260,250};
	int temp2[]={260,270,280,290};
	
	
	
	public void calculate()
	{
		
		for(int i=0;i<20;i++)
		{
			for(int j=0;j<20;j++)
			{
				
				array[i][j]=sphere.tempQuad[tempInd];
				tempInd++;
				
				
			}
			
			if(tempInd >399)
				tempInd=0;
			
			
		}
		
		
		for(int i=0;i<20;i++)
		{
			for(int j=0;j<20;j++)
			{
				if(i==0 && j==0)
				{
					for(int k=0;k<6;k++)
					{
						uArray[i][j][k] = array[i][19][k]-array[i][j][k];
						vArray[i][j][k] = array[19][j][k]-array[i][j][k];
					}
					
				}
				else if(i!=0 && j==0)
				{
					for(int k=0;k<6;k++)
					{
						uArray[i][j][k] = array[i][19][k]-array[i][j][k];
						vArray[i][j][k] = array[i-1][j][k]-array[i][j][k];
					}
					
				}
				else if(i==0 && j!=0)
				{
					for(int k=0;k<6;k++)
					{
						uArray[i][j][k] = array[i][j-1][k]-array[i][j][k];
						vArray[i][j][k] = array[19][j][k]-array[i][j][k];
					}
					
				}
				else
				{
					for(int k=0;k<6;k++)
					{
						uArray[i][j][k] = array[i][j-1][k]-array[i][j][k];
						vArray[i][j][k] = array[i-1][j][k]-array[i][j][k];
					}
					
				}

				
				
			}
			
			
		}

					
		for(int i=0;i<20;i++)
		{
			for(int j=0;j<20;j++)
			{
				
				cross(uArray[i][j],vArray[i][j],crossP[i][j]);
				
				
			}
			
			
		}			
			
		
		for(int i=0;i<20;i++)
		{
			for(int j=0;j<20;j++)
			{
				
				if(i==0 && j==19 )
				{
					for(int k=0;k<6;k++)
					{
						surfaces[i][j][k]=crossP[i][j][k]+ crossP[0][0][k]+ crossP[19][19][k]+ crossP[19][0][k];
					}
				}
				else if(i==0 && j!=19 )
				{
					for(int k=0;k<6;k++)
					{
						surfaces[i][j][k]=crossP[i][j][k]+ crossP[0][j+1][k]+ crossP[19][j][k]+ crossP[19][j+1][k];
					}
				}
				else if(i!=0 && j==19 )
				{	
					for(int k=0;k<6;k++)
					{
						surfaces[i][j][k]=crossP[i][j][k]+ crossP[i][0][k]+ crossP[i-1][j][k]+ crossP[i-1][0][k];
					}
				}
				else
				{	
					for(int k=0;k<6;k++)
					{
						surfaces[i][j][k]=crossP[i][j][k]+ crossP[i-1][j][k]+ crossP[i-1][j+1][k]+ crossP[i][j+1][k];
					}
				}				
				
				
			}
			
			
		}		
		
		
		for(int i=0;i<20;i++)
		{
			for(int j=0;j<20;j++)
			{
				
				normalize(surfaces[i][j]);
				for(int k=3,l=0;k<6;k++,l++)
					array[i][j][k]=surfaces[i][j][l];	
				
			}
			
			
		}		
		
			
	}
	
	int pasd=0;
	public void doStuff(double matrix[][])
	{


		sphere.draw(G,matrix);
		

		
		calculate();

		for(int i=0,j=0,k=0;i<800;i+=2)
		{
			if(k==19 && j!=19)
			{
				polygon[i][0]=array[j][k];
				polygon[i][1]=array[j][0];
				polygon[i][2]=array[j+1][k];
				
				polygon[i+1][0]=array[j][0];
				polygon[i+1][1]=array[j+1][k];
				polygon[i+1][2]=array[j+1][0];
	
			}
			else if(k==19 && j==19)
			{
				polygon[i][0]=array[j][k];
				polygon[i][1]=array[j][0];
				polygon[i][2]=array[0][k];
				
				polygon[i+1][0]=array[j][0];
				polygon[i+1][1]=array[0][k];
				polygon[i+1][2]=array[0][0];

	
			}			
			else if(k!=19 && j==19)
			{
				polygon[i][0]=array[j][k];
				polygon[i][1]=array[j][k+1];
				polygon[i][2]=array[0][k];
				
				polygon[i+1][0]=array[j][k+1];
				polygon[i+1][1]=array[0][k];
				polygon[i+1][2]=array[0][k+1];
				
	
			}
			else
			{
				polygon[i][0]=array[j][k];
				polygon[i][1]=array[j][k+1];
				polygon[i][2]=array[j+1][k];
				
				polygon[i+1][0]=array[j][k+1];
				polygon[i+1][1]=array[j+1][k];
				polygon[i+1][2]=array[j+1][k+1];
	
			}			
			k++;
			if(k==20)
			{
				j++;
				k=0;
				
			}
			if(j==20)
				j=0;
			
						
		}
		
		pArr=0;
		
		
		
		
	}

   	// ALL LIGHT SOURCE DIRECTION VECTORS IN THE SCENE

   	double L[][] = {{1,1,1.5},{1,0,.75}};

	// THE RENDERER

   	final double FAR_Z = -100000; // BACKGROUND Z
   	double zbuffer[], normal[] = new double[3];

   	// INITIALIZE EVERYTHING WHEN APPLET STARTS

   	public void init() 
   	{
		super.init();
      	zbuffer = new double[W*H];
      	clearZbuffer();
      
      

      

	
      	// NORMALIZE ALL POLYGON VERTEX NORMAL VECTOR LENGTHS

      	for (int n = 0 ; n < polygon.length ; n++)
	 		for (int i = 0 ; i < polygon[n].length ; i++) 
	 		{
	    		for (int k = 0 ; k < 3 ; k++)
	       			normal[k] = polygon[n][i][k+3];
	    		normalize(normal);
	    		for (int k = 0 ; k < 3 ; k++)
	       			polygon[n][i][k+3] = normal[k];
	 		}

      	// NORMALIZE ALL LIGHT SOURCE DIRECTION VECTOR LENGTHS

      	for (int i = 0 ; i < L.length ; i++)
         	normalize(L[i]);
         	
   	}

   	// THIS IS CALLED EVERY FRAME TO RENDER THE IMAGE

	double theta2=Math.PI;

	public void setPix(int frame) 
	{
      	clearZbuffer();
      	
  
	 	   	
		Matrix3D.identity(matrix);
		Matrix3D.zRotate(matrix,Math.PI/3);
		Matrix3D.zRotate(matrix,theta2);
		Matrix3D.scale(matrix,.15,.6,.5);

 		
	 	doStuff(matrix);
	 	
      	for (int n = 0 ; n < polygon.length ; n++)
	 		displayPolygon(polygon[n]);
	 		
	 	for (int n = 0 ; n < polygon.length ; n++)
	 	{
	 		for(int m=0;m<3;m++)
	 			for(int o=0;o<6;o++)
	 				polygon[n][m][o]=0;
	 	}
	 		
	 		
		Matrix3D.identity(matrix);
		Matrix3D.zRotate(matrix,-Math.PI/3);
		Matrix3D.zRotate(matrix,theta2);
		Matrix3D.scale(matrix,.15,.6,.5);
 		
	 	doStuff(matrix);
	 	
      	for (int n = 0 ; n < polygon.length ; n++)
	 		displayPolygon(polygon[n]);	 
	 		
	 	

		Matrix3D.identity(matrix);
//		Matrix3D.zRotate(matrix,Math.PI/3);
		Matrix3D.zRotate(matrix,theta2);
		Matrix3D.scale(matrix,.15,.6,.5);

 		
	 	doStuff(matrix);
	 	
      	for (int n = 0 ; n < polygon.length ; n++)
	 		displayPolygon(polygon[n]);
	 		
	 	for (int n = 0 ; n < polygon.length ; n++)
	 	{
	 		for(int m=0;m<3;m++)
	 			for(int o=0;o<6;o++)
	 				polygon[n][m][o]=0;
	 	}	



		 		 	
	 	theta2+=.05;
	
	 		
   	}

   	// CLEAR THE ZBUFFER AND IMAGE

   	void clearZbuffer() 
   	{
      	for (int i = 0 ; i < zbuffer.length ; i++)
	 		zbuffer[i] = FAR_Z;
      	for (int i = 0 ; i < W*H ; i++)
	 		pix[i] = pack(0,0,0);
   	}

   	// PROJECT ONE {X,Y,Z,Nx,Ny,Nz} VERTEX ONTO THE IMAGE PLANE,
   	// AND SHADE IT, TO CREATE A {Px,Py,Pz,Red,Green,Blue} VECTOR

   	double rgb[] = new double[3];
   	double RValue[] = new double[3];
   	double normalVec[] = new double[3];

   	void projectVertex(double pt[], double p[]) 
   	{

      // PROJECT VERTEX: X,Y,Z  ->  Px,Py,Pz

      	double x = pt[0], y = pt[1], z = pt[2];
      	double nx = pt[3], ny = pt[4], nz = pt[5];
      	
      	normalVec[0] = nx;
      	normalVec[1] = ny;
      	normalVec[2] = nz;
      	
 //     	System.out.println(nx+"  "+ny+"  "+nz);
      	x =  W * x + W/2;
      	y = -W * y + H/2;
      	p[0] = x;
      	p[1] = y;
      	p[2] = z;
      	
//      	2 (n . e) n - e

      	
      	RValue[0] = 2 * (dot(normalVec,normalizer(L[0]))) * normalVec[0] - normalizer(L[0])[0];
      	RValue[1] = 2 * (dot(normalVec,normalizer(L[0]))) * normalVec[1] - normalizer(L[0])[1];
      	RValue[2] = 2 * (dot(normalVec,normalizer(L[0]))) * normalVec[2] - normalizer(L[0])[2];

      // SHADE VERTEX: Nx,Ny,Nz  ->  Red,Green,Blue

      	doShading(pt, rgb, RValue, normalVec);
      	p[3] = rgb[0];
      	p[4] = rgb[1];
      	p[5] = rgb[2];
   	}

	
   	void doShading(double pt[], double rgb[], double RValue[], double normalVec[]) 
   	{
      	rgb[0] = rgb[1] = rgb[2] = 0.2;
      	
   //   	System.out.println(RValue[0]+"  "+RValue[1]+"  "+RValue[2]);
      	for (int i = 0 ; i < L.length ; i++) 
      	{
//        	double d = pt[3]*L[i][0] + pt[4]*L[i][1] + pt[5]*L[i][2];
//	 		if (d > 0) 
	 		{
            	rgb[0] += (.4*Math.max(0,(dot(normalVec,normalizer(L[i]))))) + (.2*(Math.pow(Math.max(0,dot(RValue,normalizer(L[i]))),16)));
            	rgb[1] += (.4*Math.max(0,(dot(normalVec,normalizer(L[i]))))) + (.2*(Math.pow(Math.max(0,dot(RValue,normalizer(L[i]))),16)));
            	rgb[2] += (.4*Math.max(0,(dot(normalVec,normalizer(L[i]))))) + (.2*(Math.pow(Math.max(0,dot(RValue,normalizer(L[i]))),16)));
         	}
      	}
   	}

   // DISPLAY ONE POLYGON

   	double p[][] = new double[30][7]; // TEMPORARY STORAGE FOR SHADED PROJECTED POLYGON

   	void displayPolygon(double polygon[][]) 
   	{
      	int npts = polygon.length, x;
      	double t, pL[] = new double[6], pR[] = new double[6];

      	// FIND TOP AND BOTTOM SCAN LINES

      	int ymin = H;
      	int ymax = 0;
      	for (int i = 0 ; i < npts ; i++) 
      	{
	 		projectVertex(polygon[i], p[i]);
         	ymin = Math.min(ymin, (int)p[i][1]);
         	ymax = Math.max(ymax, (int)p[i][1]);
      	}

      	// LOOP THROUGH ALL SCAN LINES CONTAINING POLYGON

      	for (int y = ymin ; y <= ymax ; y++) 
      	{

	 		// FIND LEFT AND RIGHT EDGES OF POLYGON.
	 		// P[0,1,2,3,4,5] IS {Px,Py,Pz,Red,Green,Blue}

         	pL[0] = W;
         	pR[0] = 0;
         	for (int i = 0 ; i < npts ; i++) 
         	{
	    		int j = (i+1) % npts;
	    		if (p[i][1] < y != p[j][1] < y) 
	    		{
	       			t = (y - p[i][1]) / (p[j][1] - p[i][1]);
	       			x = (int)lerp(t, p[i][0], p[j][0]);
	       			if (x < pL[0])
		  				for (int k = 0 ; k < 6 ; k++)
		     				pL[k] = lerp(t, p[i][k], p[j][k]);
	       			if (x >= pR[0])
		  				for (int k = 0 ; k < 6 ; k++)
		     				pR[k] = lerp(t, p[i][k], p[j][k]);
	    		}
	 		}

	 		// LOOP THRU PIXELS IN SCAN LINE

        	for (x = (int)pL[0] ; x < (int)pR[0] ; x++) 
        	{
	    		int i = xy2i(x,y);

	    		// INTERPOLATE TO GET Z AT THIS PIXEL

	    		t = (x - pL[0]) / (pR[0] - pL[0]);
	    		double z = lerp(t, pL[2], pR[2]);

	    		// IF Z IS CLOSER, REPLACE PIXEL IN ZBUFFER AND IMAGE
          		if (z > zbuffer[i]) 
          		{
             		zbuffer[i] = z;
               		pix[i] = pack(f2i(lerp(t, pL[3], pR[3])),
                             	f2i(lerp(t, pL[4], pR[4])),
                             	f2i(lerp(t, pL[5], pR[5])));
            	}
        	}
      	}
   	}

// GENERIC UTILITY ROUTINES

   // NORMALIZE A VECTOR

   	void normalize(double v[]) 
   	{
      	double s = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
      	v[0] /= s;
      	v[1] /= s;
      	v[2] /= s;
   	}
   	
   
   	public double[] normalizer(double v[]) 
   	{
      	double s = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
      	v[0] /= s;
      	v[1] /= s;
      	v[2] /= s;
      	
      	return v;
   	}  

   // LINEAR INTERPOLATION

   	double lerp(double t, double a, double b) 
   	{
      	return a + t * (b - a);
   	}

   // CONVERT A FLOATING POINT VALUE INTO A 0..255 INTEGER

   	int f2i(double t) 
   	{
      	return Math.max(0, Math.min(255, (int)(255 * t)));
   	}
   
   
   	public static void cross(double a[], double b[], double dst[]) 
   	{
      	dst[0] = a[1]*b[2] - a[2]*b[1];
      	dst[1] = a[2]*b[0] - a[0]*b[2];
      	dst[2] = a[0]*b[1] - a[1]*b[0];
   	}
   
   	public static double dot(double a[], double b[]) 
   	{
      	return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
   	}

}

