import java.awt.Graphics;
import point3d;
import vector3d;
import defines;
import matrix3d;
import globals;
import linetype;
//import FixAsm;
//import java.lang.Number;

class panel3d{


	protected point3d[] vpoint = new point3d[4];
	protected point2d[] spoint = new point2d[5];
	protected Vector normal = new Vector();
	protected int spcount, invis;
	protected int color, padding;
	protected double radius;
	protected defines def = new defines();
	protected globals glob = new globals();
	protected CeilLine linetype = new CeilLine();

	//---------------------------------------------------------

	public panel3d()
	{
	/*	for(int i=0;i<4;i++)
			vpoint[i]=new point3d(); */
		for(int i=0;i<5;i++)
			spoint[i]=new point2d();
		invis = 0;
		color = 0;
	}

	//---------------------------------------------------------

	protected void CalcRadius()
	{
		// Calculate teh radius of the panel:
		// Initialize/declare variables:
		point3d center = new point3d();
		int count;
		double dist;
		double distance[];
		distance = new double[4];
		point3d[] tempPoint = new point3d[4];
		/*for(int i=0; i<4;i++)
			tempPoint[i]=new point3d();	*/

		// Create a temporary vertex list:
		for(count =0; count < 4; count++)
		{
			tempPoint[count]=vpoint[count].clone();
		}

		// calculate center of polygon:
		for(count =0; count < 4; count++)
		{
			center.plusequal( tempPoint[count]);
		}

		center.divideequal(4.0);

		// Translate polygon to it's center:
		for(count =0; count <4; count++)
		{
			tempPoint[count].minusequal(center);
		}

		// Calculate the distance to each of the verticies:
		for(count = 0; count < 4; count++)
		{
			dist = tempPoint[count].Mag();
			distance[count]=dist;
		}

		// Determine the maximum distance:
		dist = distance[0];
		for(count=1; count<4; count++)
		{
			if(distance [count] > dist)
				dist = distance[count];
		}

		// Dist holds the maximum radius of the panel:
		radius = dist;

	} // CalcRadius()
	//----------------------------------

	protected double CalcCenterZ()
	{
		// Calculate the polygon's center Z:
		double Sum = vpoint[0].Wz + vpoint[1].Wz + vpoint[2].Wz + vpoint[3].Wz;
		Double sum2 = new Double(Sum);
		long SummedComponents = sum2.longValue();
		long CenterZ = (SummedComponents >> 2);
		return CenterZ;
	} // CalcCenterZ()
	//----------------------------------

	protected boolean CalcBFace()
	{
		// Determine if polygon is a backface:
		boolean Visible = true;
		invis = 0;
		point3d v = vpoint[0];
		double direction = (v.Wx * (normal.Tx - vpoint[0].Wx)+
						    v.Wy * (normal.Ty - vpoint[0].Wy)+
							v.Wz * (normal.Tz - vpoint[0].Wz));

		if (direction > 0.0)
		{
			// Get the cosine of the angle between the viewer and the polygon normal
			direction /= v.Mag();
			// Assume panel will remain a time proportional to the angle between the
			// view to the polygon normal:
			Double d = new Double(direction * 25);
			invis = d.intValue();
			// Set the invisible flag for this frame:
			Visible = false;

		}
		return Visible;
	} // CalcBFace()
	//-----------------------------------------

	protected void CalcNormal()
	{
		// Calculates the normal of the panel:
		double x1, y1, z1, x2, y2, z2, x3, y3, z3, distance, a, b, c;
		short count, range =0;
		point3d TVert = new point3d();
		point3d[] UniqueVerts = new point3d[4];
	/*	for(int i=0;i<4;i++)
			UniqueVerts[i]=new point3d();
	   */
		// Create a list of unique vertices:
		for(count =0; count < 4; count++)
		{
			TVert = vpoint[count];
			if(range == 0)
				UniqueVerts[range++]=TVert.clone();
			else if(TVert.UniqueVert(UniqueVerts, range))
			{
				UniqueVerts[range++]=TVert.clone();
			}
		}
		x1 = UniqueVerts[0].Lx;
		y1 = UniqueVerts[0].Ly;
		z1 = UniqueVerts[0].Lz;

		x2 = UniqueVerts[1].Lx;
		y2 = UniqueVerts[1].Ly;
		z2 = UniqueVerts[1].Lz;

		x3 = UniqueVerts[2].Lx;
		y3 = UniqueVerts[2].Ly;
		z3 = UniqueVerts[2].Lz;
	 
		// Use plane equation to determine plane's orientation:
		a = y1*(z2-z3) + y2*(z3-z1) + y3*(z1-z2);
		b = z1*(x2-x3) + z2*(x3-x1) + z3*(x1-x2);
		c = x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2);

		// Get the distance to the vector:
		distance = Math.sqrt(a*a + b*b + c*c);

		// Normalize the normal to 1 and create a point;
		normal.x = (a/distance) + vpoint[0].Lx;
		normal.y = (b/distance) + vpoint[0].Ly;
		normal.z = (c/distance) + vpoint[0].Lz;
	  
	} // CalcNormal()
	//-----------------------------------------

	protected boolean CheckExtents()
	{
		//Determine if panel is in the range of MINZ to MAXZ:
		int count;
		boolean Visible=false;
		double MinZ;
		for(count =0; count < 4; count++)
		{
			if(vpoint[count].Wz > def.MINZ)
			{
				Visible = true;
				invis = 0;
				break;
			}
		}
		if(Visible)
		{
			MinZ = vpoint[0].Wz;
			for(count=1; count<4; count++)
			{
				if(vpoint[count].Wz < MinZ)
					MinZ = vpoint[count].Wz;
			}
			if(MinZ > def.MAXZ)
			{
				// Set the invisible flag for this frame:
				Visible = false;
				// Assume panel will remain invisible for a time
				// proportional to the distance from the viewer:
				Double d = new Double((MinZ - def.MAXZ)/50.0);
				invis = d.intValue();

				//invis = (MinZ - def.MAXZ) /50;
			}
		}
		else
		{
			// Assume panel will remain invisible for a time
			// proportional to the distance from the viewer:
			Double d = new Double( Math.abs(CalcCenterZ())/50.0);
			invis = d.intValue();
		}
		return Visible;
	} // CheckExtents()
	//-----------------------------------------

	protected boolean CalcVisible3D()
	{
		// Determines panel's visibility in 3D
		
		// Perform 3D culling:

		// Assume panel is visible:
		boolean Visible;

		// Perform a back face culling operation:
		Visible = CalcBFace();

		// If panel still visible, perform extent test:
		if(Visible)
			Visible = CheckExtents();
		return Visible;

	} // CalcVisible3D()

	//------------------------------------------

	protected boolean CalcVisible2D()
	{
		// Determines panel's visibility in 2D

		// Perform 2D culling:

		// Assume panel is visible:
		long XMinInVis = 0, XMaxInVis =0, YMinInVis =0, YMaxInVis =0;
		long AveX =0, AveY =0;
		int N;
		boolean Visible = true;
		invis =0;
		
		// Make sure the panel has more than two points:
		if(spcount < 3)
		{
			// If not, flag panel is invisible:
			Visible =false;

			// Assume panel will remain invisible fore four more frames:
			invis = 4;
			return Visible;
		}// if
	
		// Determine location of panel's 2D points:
		for(N=0; N<spcount; N++)
		{
			if(spoint[N].x < def.MINX)
				XMinInVis++;
			if(spoint[N].x>def.MAXX)
				XMaxInVis++;

			if(spoint[N].y<def.MINY)
				YMinInVis++;
			if(spoint[N].y > def.MAXY)
				YMaxInVis++;

			AveX += spoint[N].x;
			AveY += spoint[N].y;
		}// for

		if(XMinInVis >= spcount)
		{
			// Assume panel will remain invisible for a time
			// proportional to the distance from the edge of the
			// panel to the edge of the viewport:
			
			AveX /= spcount;
			Long d = new Long(Math.abs(AveX / (320*26)));
			invis = d.intValue();
			Visible=false;
		}// if
		else if(YMinInVis >= spcount)
		{
			AveY /= spcount;
			Long d = new Long(Math.abs(AveY / (200*26)));
			invis = d.intValue();
			Visible=false;
		}// else if
		else if(XMaxInVis >= spcount)
		{
			AveX /= spcount;
			Double d = new Double( (AveX - def.MAXX) / (320*26) );
			invis = d.intValue();
			Visible=false;
		}// else if
		else if(YMaxInVis >= spcount)
		{
			AveY /= spcount;
			Double d = new Double( (AveY - def.MAXY) / (200*26) );
			invis = d.intValue();
			Visible = false;
		}// else if
		return Visible;

	} // Calcvisible2D()

	//-------------------------------------------

	public void Update(matrix3d M)
	{
		// Update normal and misc. information

		// Transform the normal:
		M.Transform(normal);

		// Update the invisibility flag:
		if(invis > 0)
			invis--;
	} // Update()

	//---------------------------------------------

	public void Display(int g[])
	{
	 
		// Rasterize panel

		// Display the panel in the screen buffer g:
		int RColor, dptr, zptr;
	//	char DPtr;
		CeilLine LeftSeg = new CeilLine();
		CeilLine RightSeg = new CeilLine();
		long Top=0, N, RightPos, LeftPos, NewRightPos, NewLeftPos,
			Height, EdgeCount, YIndex, Width, XStart, XEnd, DeltaZ, ZStep;
		long /*ZPtr,*/ Z, ZColor;


		long[] XSTART = new long[1];
		long[] XEND = new long[1];
		long[] Zarray = new long[1];

		

		RColor = color;
		EdgeCount = spcount;

		// Search for lowest Y coordinate (top of polygon):
		for(N = 1; N<spcount; N++)
		{
			if(spoint[(int)N].y < spoint[(int)Top].y)
				Top = N;
		}
		RightPos = Top;
		LeftPos = Top;

		

		// Calculate the index to the buffer:
		YIndex = spoint[(int)Top].y * 320;

		// Loop for all polygon edges:
		while(EdgeCount > 0)
		{
			// Determine if the right side of the polygon needs
			//   (re)initializing:
			
					
			if(RightSeg.Height() <= 0)
			{
				NewRightPos = RightPos +1;
				if(NewRightPos >= spcount)
					NewRightPos = 0;
				RightSeg.Init(spoint[(int)RightPos], spoint[(int)NewRightPos]);
				RightPos = NewRightPos;
				EdgeCount--;
				
				// Perform object-precision clip on top of edge (if necessary)
				if(RightSeg.GetY() < def.MINY)
				{
					RightSeg.ClipTop((int)def.MINY);
					YIndex = (long)(def.MINY * 320);
				}
			}
			// Determine if the left side of the polygon needs
			//  (re)initializing:
			if(LeftSeg.Height() <=0)
			{
				NewLeftPos = LeftPos -1;
				if(NewLeftPos < 0)
					NewLeftPos = (spcount -1);
				LeftSeg.Init(spoint[(int)LeftPos], spoint[(int)NewLeftPos]);
				LeftPos = NewLeftPos;
				EdgeCount--;
				// Perform object-percision clip on top of edge (if necessary)
				if(LeftSeg.GetY() < def.MINY)
				{
					LeftSeg.ClipTop((int)def.MINY);
					YIndex = (long)(def.MINY * 320);
				}
			}

			// Subdivide polygon into trapezoid:
			if(LeftSeg.Height() < RightSeg.Height())
			{
				Height = LeftSeg.Height();
				if((LeftSeg.GetY() + Height) > def.MAXY)
				{
					Height = (long)(def.MAXY - LeftSeg.GetY());
					EdgeCount=0;
				}
			}
			else
			{
				Height = RightSeg.Height();
				if((RightSeg.GetY() + Height) > def.MAXY)
				{
					Height = (long)(def.MAXY - RightSeg.GetY());
					EdgeCount=0;
				}
			}

			//Loop for the height of the trapezoid:
			
			//g[10001]=(int)Height;
			while(Height-- > 0)
			{
				// Calculate initial values:
				XStart = LeftSeg.GetX();
				XEnd = RightSeg.GetX();
				Width = XEnd - XStart;
				if(Width > 0)
				{
					Z = LeftSeg.GetZ();
					DeltaZ = (RightSeg.GetZ() - LeftSeg.GetZ());
					ZStep = DeltaZ / Width;
					// Clip the scan-line
					

					XSTART[0]=XStart;
					XEND[0]=XEnd;
					Zarray[0]=Z;

					//long[] ZSTEP = new
					ClipHLine(XSTART, XEND, Zarray, ZStep);
					
					XStart=XSTART[0];
					XEnd = XEND[0];
					Z = Zarray[0];

					
					Width = XEnd - XStart;
					dptr = (int)(YIndex + XStart);
					zptr = (int)(YIndex + XStart);
					
//					g[10000]=(int)Z;
//					g[10001]=(int)glob.ZBuffer[zptr];

					// Loop for width of scan-line
					while(Width-- >0)
					{		
						

						if(glob.ZBuffer[zptr] < Z)
						{
							glob.ZBuffer[zptr] = Z;
							ZColor = 255 - 5*((int)((1<<glob.ZSTEP_PREC) / (Z - glob.ZTrans) / ((def.MAXZ) / 256)));
							if(ZColor > 255)
								ZColor=255;
							if(ZColor < 1)
								ZColor=1;
						
							ZColor= (ZColor + Math.abs(RColor))/2;
							g[dptr] = (int)( (255 << 24) | (ZColor << 16) | (ZColor << 8) | (ZColor));
						//	g[dptr] = (255 << 24) | (255 << 16) | (255 << 8) | (255);
						//	g[dptr] = (int)(ZColor << 24) | (255 << 16) | (255 << 8) | (255);
						}
						Z+=ZStep;
						dptr++;
						zptr++;
					}

				}
			/*	if((XStart > def.MINX) && (XEnd >=def.MINX) && (XStart < def.MAXX) && (XEnd<def.MAXX))
				{
					g[(int)(YIndex+XStart)] = (int)( (255 << 24) | (255 << 16) | (255 << 8) | (0));
					g[(int)(YIndex+XEnd)] = (int)( (255 << 24) | (255 << 16) | (255 << 8) | (0));
				}
			  */

				RightSeg.increment();
				LeftSeg.increment();
				YIndex += 320;
			}

		}

//		for(int k=0;k<spcount;k++)
//		{
//			g[k*3]=(int)spoint[k].x;
//			g[k*3+1]=(int)spoint[k].y;
//			g[k*3+2]=/*(int)spoint[k].z;*/ (int)((1<<glob.ZSTEP_PREC) / (spoint[k].z - glob.ZTrans));
//		}
 
/*		long x,y;
		for(int j = 0; j<spcount;j++)
		{
			x=spoint[j].x;
			y=spoint[j].y;
			if( (x>=def.MINX) && (x<=def.MAXX) && (y>=def.MINY) && (y<=def.MAXY))
			{
				g[(int)(x + (y*320))] = (255 << 24) | (255 << 16) | (255 << 8) | (0);
			}
		}
  */


	}// Display()


	//-------------------------------------------------------

	public void Project(int g[])
	{
		// Project panel onto screen

		//Perform front Z-clipping and project the panel's 3D points

		spcount = 4;
		int count, outcount=0, startI, endI;
		double OneOverZ;
		Point3d ZClipPoint[] = new Point3d[5];
		for(int i=0;i<5;i++)
			ZClipPoint[i]=new Point3d();

		// Initialize pointer to last vertes:
		startI = spcount-1;

		// Loop through all edges of panel using S&H algorithm:
		for(endI = 0; endI < spcount; endI++)
		{
			if(vpoint[startI].Wz >= def.MINZ)
			{
				if(vpoint[endI].Wz >= def.MINZ)
				{
					//Entirely inside front view volume (case 1)
					//   output unchanged
					ZClipPoint[outcount].Wx = vpoint[endI].Wx;
					ZClipPoint[outcount].Wy = vpoint[endI].Wy;
					ZClipPoint[outcount].Wz = vpoint[endI].Wz;

					outcount++;
				}
				else{
					//SPoint is leaving view volume (case 2)
					//  clip using parametric form of line:

					double DeltaZ = (vpoint[endI].Wz - vpoint[startI].Wz);

					double t = (def.MINZ - vpoint[startI].Wz)/DeltaZ;
					ZClipPoint[outcount].Wx = vpoint[startI].Wx+(vpoint[endI].Wx - vpoint[startI].Wx)*t;
					ZClipPoint[outcount].Wy = vpoint[startI].Wy+(vpoint[endI].Wy - vpoint[startI].Wy)*t;
					ZClipPoint[outcount].Wz = def.MINZ;
					//Update index:
					outcount++;

				}
			}
			else
			{
				if(vpoint[endI].Wz >= def.MINZ)
				{					  
					// SPoint is entering view volume (case 3)
					//  clip using parametric form of line:

					double DeltaZ = (vpoint[endI].Wz - vpoint[startI].Wz);
					double t = (def.MINZ - vpoint[startI].Wz)/DeltaZ;
					ZClipPoint[outcount].Wx = vpoint[startI].Wx+(vpoint[endI].Wx - vpoint[startI].Wx)*t;
					ZClipPoint[outcount].Wy = vpoint[startI].Wy+(vpoint[endI].Wy - vpoint[startI].Wy)*t;
					ZClipPoint[outcount].Wz = def.MINZ;
					//Update index:
					outcount++;

					// Add an extra edge to list:
					ZClipPoint[outcount].Wx = vpoint[endI].Wx;
					ZClipPoint[outcount].Wy = vpoint[endI].Wy;
					ZClipPoint[outcount].Wz = vpoint[endI].Wz;
					// Update index;
					outcount++;
				}
				else
				{
					// No operation is necessary for case 4
				}
			}
			// Advance to next vertex:
			startI = endI;
		}

		// Store the number of vertices in outcount:
		spcount = outcount;

		// Project panel's points:
		for(count=0; count < outcount; count++)
		{
//			for(int c=1500;c<1550;c++)
//				g[c] = (255 << 24) | (255 << 16) | (0 << 8) | (0);
			OneOverZ = 1.0 / ZClipPoint[count].Wz;
			Double d = new Double(ZClipPoint[count].Wx * def.XSCALE* OneOverZ + 160.0);
			Double d2 = new Double(ZClipPoint[count].Wy * def.YSCALE* OneOverZ + 100.0);
			spoint[count].x = d.longValue();
			spoint[count].y = d2.longValue();
			spoint[count].z = (long)(OneOverZ * (double)(1 << glob.ZSTEP_PREC));
		}
		//g[10000]=(int)vpoint[3].Wz;
		//g[10001]=(int)vpoint[0].Wz;

	}// Project()

	//-------------------------------------------------------
	
	public void InitPosition()
	{
		CalcNormal();
		CalcRadius();
	}// InitPosition()

	//-------------------------------------------------------

	public void CalcInten()
	{
		// Calculates panel's intensity

		double Mag = (Math.sqrt(glob.Light.X * glob.Light.X +
								glob.Light.Y * glob.Light.Y + 
								glob.Light.Z * glob.Light.Z));

		// Assign a color based on normal:
		double CosA = ((normal.x - vpoint[0].Lx) * glob.Light.X +
					   (normal.y - vpoint[0].Ly) * glob.Light.Y +
					   (normal.z - vpoint[0].Lz) * glob.Light.Z) / Mag;
		Double d = new Double(CosA * def.COLOR_RANGE + def.COLOR_START);
		color = d.intValue();
	}// CalcInten()

	//-------------------------------------------------------

	public int Invisible()
	{
		return invis;
	}// Invisible()

	//-------------------------------------------------------

	public boolean IsVisible2D()
	{
		return CalcVisible2D();
	}// IsVisible2d


	//-------------------------------------------------------

	public boolean IsVisible3D()
	{
		return CalcVisible3D();
	}// IsVisible3d

	//----------------------------------------------------------

	public void ClipHLine(long X1[], long X2[], long Z[], long ZStep)
	{
		// Clip horizontal "Z-buffered" line
		if(X1[0] < def.MINX)
		{
			// Take advantage of the fact that (a*(b*f)/f) = (a*b)
			Z[0]+= ZStep * (long)(def.MINX - X1[0]);
			X1[0] = (long)def.MINX;
		}
		else if(X1[0] > def.MAXX)
			X1[0] = (long)def.MAXX;
		if(X2[0] < def.MINX)
			X2[0] = (long)def.MINX;
		else if(X2[0] > def.MAXX)
			X2[0] = (long)def.MAXX;
	}// ClipHLine

};
