import java.awt.*;
import java.applet.*;
import java.util.*;
import java.lang.*;

public class CG_hw_07_final 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[2000][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 cArray[][] = new double[2000][3];
	double normalV[][] = new double[2000][3];
	
	
	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 >380)
				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()
	{
		Matrix3D.identity(matrix);
		Matrix3D.scale(matrix,.5,.5,.5);
//		Matrix3D.zRotate(matrix,theta);

		sphere.draw(G,matrix);
		
		theta+=.05;
		
		calculate();

		for(int i=0,j=0,k=0;i<850;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}
   };

// 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

   public void setPix(int frame) {
      clearZbuffer();
      doStuff();
      
      for (int n = 0 ; n < polygon.length ; n++)
	 		displayPolygon(polygon[n]);
   }

   // 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];

   void projectVertex(double pt[], double p[]) {

      // PROJECT VERTEX: X,Y,Z  ->  Px,Py,Pz

      double x = pt[0], y = pt[1], z = pt[2];
      x =  W * x + W/2;
      y = -W * y + H/2;
      p[0] = x;
      p[1] = y;
      p[2] = z;

      // SHADE VERTEX: Nx,Ny,Nz  ->  Red,Green,Blue

      doShading(pt, rgb);
      p[3] = rgb[0];
      p[4] = rgb[1];
      p[5] = rgb[2];
   }

   // VERY ELEMENTARY VERTEX SHADER (JUST A PLACE-HOLDER)
   // THIS SHOULD BE REPLACED BY A TRUE PHONG SHADING ALGORITHM

   void doShading(double pt[], double rgb[]) {
      rgb[0] = rgb[1] = rgb[2] = 0.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] += .8 * d;
            rgb[1] += .4 * d;
            rgb[2] += .5 * d;
         }
      }
   }

   // 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;
   }
   
   void normalizer(double a[],double v[]) {
      double s = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]) + Math.sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
      v[0] /= s;
      v[1] /= s;
      v[2] /= s;
   }  

   // 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];
   }

}

