/*
 * @(#)object3d.java        1.0 2002/08/01
 *
 * Copyright 2001-2006 by Danger Research, Inc.
 * 3101 Park Blvd, Palo Alto, CA  94301
 * All rights reserved.
 */
package com.danger.test3d;

import danger.ui.Bitmap;
import danger.ui.Pen;
import danger.util.*;


class object3d
{
	//~ Instance fields --------------------------------------------------------

	private int				  mNumVertexes;
	private point3d[]		  mObjVertex;
	private point3d[]		  mTransformedVertex;
	private int				  mNumLineSegments;
	private lineSegment[]     mLineSegments;
	private int				  mNumSurfaces;
	private surface[]		  mSurfaces;
	private transformMatrix3d mTMatrix;
	private point3d			  mTempPoint;
	private int				  mLastScale;
	private int				  mLastRotateX;
	private int				  mLastRotateY;
	private int				  mLastRotateZ;
	private int				  mLastTranslateX;
	private int				  mLastTranslateY;
	private int				  mLastTranslateZ;

	//~ Constructors -----------------------------------------------------------

	/**
	 * Creates a new object3d object.
	 *
	 * @param numVertexes DOCUMENT ME!
	 * @param numLineSegs DOCUMENT ME!
	 * @param numSurfaces DOCUMENT ME!
	 */
	public object3d(int numVertexes, int numLineSegs, int numSurfaces)
	{
		mNumVertexes		   = numVertexes;
		mObjVertex			   = new point3d[numVertexes];
		mTransformedVertex     = new point3d[numVertexes];

		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i] = new point3d();
		}

		mLineSegments		 = new lineSegment[numLineSegs];
		mNumLineSegments     = numLineSegs;

		for (int i = 0; i < mNumLineSegments; i++)
		{
			mLineSegments[i] = new lineSegment();
		}

		mSurfaces		 = new surface[numSurfaces];
		mNumSurfaces     = numSurfaces;

		for (int i = 0; i < numSurfaces; i++)
		{
			mSurfaces[i] = new surface();
		}

		mTMatrix	   = new transformMatrix3d();
		mTempPoint     = new point3d();

		mLastScale		    = Fixed.fromInt(1);
		mLastRotateX	    = 0;
		mLastRotateY	    = 0;
		mLastRotateZ	    = 0;
		mLastTranslateX     = 0;
		mLastTranslateY     = 0;
		mLastTranslateZ     = 0;
	}

	//~ Methods ----------------------------------------------------------------

	/**
	 * DOCUMENT ME!
	 *
	 * @param vertexNum DOCUMENT ME!
	 * @param vertex DOCUMENT ME!
	 */
	public void setVertex(int vertexNum, point3d vertex)
	{
		mObjVertex[vertexNum] = vertex;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param segNum DOCUMENT ME!
	 * @param startVertex DOCUMENT ME!
	 * @param endVertex DOCUMENT ME!
	 */
	public void setLineSegment(int segNum, int startVertex, int endVertex)
	{
		mLineSegments[segNum].startVertex     = startVertex;
		mLineSegments[segNum].endVertex		  = endVertex;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param surfaceNum DOCUMENT ME!
	 * @param surfaceTexture DOCUMENT ME!
	 */
	public void setSurfaceTexture(int surfaceNum, Bitmap surfaceTexture)
	{
		mSurfaces[surfaceNum].texture = surfaceTexture;

		mSurfaces[surfaceNum].u = new int[3];
		mSurfaces[surfaceNum].v = new int[3];

		int w = surfaceTexture.getWidth();
		int h = surfaceTexture.getHeight();

		// source is just a triangle of the full rectangle of the bitmap.
		mSurfaces[surfaceNum].u[0] = 1;
		mSurfaces[surfaceNum].v[0] = 1;
		mSurfaces[surfaceNum].u[1] = w-1;
		mSurfaces[surfaceNum].v[1] = h-1;
		mSurfaces[surfaceNum].u[2] = 1;
		mSurfaces[surfaceNum].v[2] = h-1;
/*
		mSurfaces[surfaceNum].u[0] = 0;
		mSurfaces[surfaceNum].v[0] = 0;
		mSurfaces[surfaceNum].u[1] = w;
		mSurfaces[surfaceNum].v[1] = 0;
		mSurfaces[surfaceNum].u[2] = w;
		mSurfaces[surfaceNum].v[2] = h;
		mSurfaces[surfaceNum].u[3] = 0;
		mSurfaces[surfaceNum].v[3] = h;
*/
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param surfaceNum DOCUMENT ME!
	 * @param vertexList DOCUMENT ME!
	 */
	public void setSurface(int surfaceNum, int[] vertexList)
	{
		mSurfaces[surfaceNum].vertexes     = vertexList;
		mSurfaces[surfaceNum].x			   = new int[vertexList.length];
		mSurfaces[surfaceNum].y			   = new int[vertexList.length];
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param surfaceNum DOCUMENT ME!
	 * @param vertex1 DOCUMENT ME!
	 * @param vertex2 DOCUMENT ME!
	 * @param vertex3 DOCUMENT ME!
	 */
	public void setSurface(int surfaceNum, int vertex1, int vertex2, int vertex3)
	{
		int[] list;
		list							   = new int[3];
		list[0]							   = vertex1;
		list[1]							   = vertex2;
		list[2]							   = vertex3;
		mSurfaces[surfaceNum].vertexes     = list;
		mSurfaces[surfaceNum].x			   = new int[3];
		mSurfaces[surfaceNum].y			   = new int[3];
	}

	/**
	 * DOCUMENT ME!
	 */
	public void startTransformation()
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = mObjVertex[i].x;
			mTransformedVertex[i].y     = mObjVertex[i].y;
			mTransformedVertex[i].z     = mObjVertex[i].z;
		}
	}

	/**
	 * DOCUMENT ME!
	 */
	public void endTransformation()
	{
		// convert the translated values to ints
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = Fixed.toInt(mTransformedVertex[i].x);
			mTransformedVertex[i].y     = Fixed.toInt(mTransformedVertex[i].y);
			mTransformedVertex[i].z     = Fixed.toInt(mTransformedVertex[i].z);
		}

		// currently does nothing
		if (false)
		{
			DEBUG.p("EndTransformation: ");

			for (int i = 0; i < mNumVertexes; i++)
			{
				DEBUG.p(" " + i + " " + Fixed.toInt(mTransformedVertex[i].x) +
					"." + Fixed.getFraction(mTransformedVertex[i].x) + " " +
					Fixed.toInt(mTransformedVertex[i].y) + "." +
					Fixed.getFraction(mTransformedVertex[i].y) + " " +
					Fixed.toInt(mTransformedVertex[i].z) + "." +
					Fixed.getFraction(mTransformedVertex[i].z));
			}
		}
	}

	private void multiplyMatrixAllVertexes(matrix3d matrix)
	{
		// do the matrix multiply
		for (int i = 0; i < mNumVertexes; i++)
		{
			if (false)
			{
				DEBUG.p("Before Rotate " + i + " " +
					Fixed.toInt(mTransformedVertex[i].x) + "." +
					Fixed.getFraction(mTransformedVertex[i].x) + " " +
					Fixed.toInt(mTransformedVertex[i].y) + "." +
					Fixed.getFraction(mTransformedVertex[i].y) + " " +
					Fixed.toInt(mTransformedVertex[i].z) + "." +
					Fixed.getFraction(mTransformedVertex[i].z));
				matrix.PrintFixed();
			}

			matrix.MultiplyBy(mTransformedVertex[i], mTempPoint);

			if (false)
			{
				DEBUG.p("Rotate " + i + " " + Fixed.toInt(mTempPoint.x) + "." +
					Fixed.getFraction(mTempPoint.x) + " " +
					Fixed.toInt(mTempPoint.y) + "." +
					Fixed.getFraction(mTempPoint.y) + " " +
					Fixed.toInt(mTempPoint.z) + "." +
					Fixed.getFraction(mTempPoint.z));
			}

			mTransformedVertex[i].x     = mTempPoint.x;
			mTransformedVertex[i].y     = mTempPoint.y;
			mTransformedVertex[i].z     = mTempPoint.z;
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param angle DOCUMENT ME!
	 */
	public void rotateX(int angle)
	{
		mTMatrix.Clear();
		mTMatrix.SetRotationX(angle);
		multiplyMatrixAllVertexes(mTMatrix);
		mLastRotateX = angle;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param angle DOCUMENT ME!
	 */
	public void rotateY(int angle)
	{
		mTMatrix.Clear();
		mTMatrix.SetRotationY(angle);
		multiplyMatrixAllVertexes(mTMatrix);
		mLastRotateY = angle;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param angle DOCUMENT ME!
	 */
	public void rotateZ(int angle)
	{
		mTMatrix.Clear();
		mTMatrix.SetRotationZ(angle);
		multiplyMatrixAllVertexes(mTMatrix);
		mLastRotateZ = angle;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param shearX DOCUMENT ME!
	 * @param shearY DOCUMENT ME!
	 */
	public void shearXY(int shearX, int shearY)
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = mTransformedVertex[i].x +
				Fixed.multiply(mTransformedVertex[i].z, shearX);
			mTransformedVertex[i].y     = mTransformedVertex[i].y +
				Fixed.multiply(mTransformedVertex[i].z, shearY);
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param shearX DOCUMENT ME!
	 * @param shearZ DOCUMENT ME!
	 */
	public void shearXZ(int shearX, int shearZ)
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = mTransformedVertex[i].x +
				Fixed.multiply(mTransformedVertex[i].y, shearX);
			mTransformedVertex[i].z     = mTransformedVertex[i].z +
				Fixed.multiply(mTransformedVertex[i].y, shearZ);
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param shearY DOCUMENT ME!
	 * @param shearZ DOCUMENT ME!
	 */
	public void shearYZ(int shearY, int shearZ)
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].y     = mTransformedVertex[i].y +
				Fixed.multiply(mTransformedVertex[i].x, shearY);
			mTransformedVertex[i].z     = mTransformedVertex[i].z +
				Fixed.multiply(mTransformedVertex[i].x, shearZ);
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param scale DOCUMENT ME!
	 */
	public void scale(int scale)
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = Fixed.multiply(mTransformedVertex[i].x,
					scale);
			mTransformedVertex[i].y     = Fixed.multiply(mTransformedVertex[i].y,
					scale);
			mTransformedVertex[i].z     = Fixed.multiply(mTransformedVertex[i].z,
					scale);
		}

		mLastScale = scale;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param scale DOCUMENT ME!
	 */
	public void incScale(int scale)
	{
		scale(mLastScale + scale);
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param x DOCUMENT ME!
	 * @param y DOCUMENT ME!
	 * @param z DOCUMENT ME!
	 */
	public void translate(int x, int y, int z)
	{
		for (int i = 0; i < mNumVertexes; i++)
		{
			mTransformedVertex[i].x     = mTransformedVertex[i].x + x;
			mTransformedVertex[i].y     = mTransformedVertex[i].y + y;
			mTransformedVertex[i].z     = mTransformedVertex[i].z + z;
		}

		mLastTranslateX     = x;
		mLastTranslateY     = y;
		mLastTranslateZ     = z;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param x DOCUMENT ME!
	 * @param y DOCUMENT ME!
	 * @param z DOCUMENT ME!
	 */
	public void incTranslate(int x, int y, int z)
	{
		translate(mLastTranslateX + x, mLastTranslateY + y, mLastTranslateZ +
			z);
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param inPen DOCUMENT ME!
	 */
	public void draw(Pen inPen)
	{
		point3d pointA;
		point3d pointB;

		for (int i = 0; i < mNumLineSegments; i++)
		{
			pointA     = mTransformedVertex[mLineSegments[i].startVertex];
			pointB     = mTransformedVertex[mLineSegments[i].endVertex];

			//System.out.println("line #" + i + ": " + pointA.x + " " + pointA.y + " - " + pointB.x + " " + pointB.y);
			inPen.drawLine(pointA.x, pointA.y, pointB.x, pointB.y);
		}

		for (int i = 0; i < mNumSurfaces; i++)
		{
			/* determine if this surface is facing the camera or not */
			/* find u */
			int ux = mTransformedVertex[mSurfaces[i].vertexes[0]].x -
				mTransformedVertex[mSurfaces[i].vertexes[1]].x;
			int uy = mTransformedVertex[mSurfaces[i].vertexes[0]].y -
				mTransformedVertex[mSurfaces[i].vertexes[1]].y;

			/* find v */
			int vx = mTransformedVertex[mSurfaces[i].vertexes[2]].x -
				mTransformedVertex[mSurfaces[i].vertexes[1]].x;
			int vy = mTransformedVertex[mSurfaces[i].vertexes[2]].y -
				mTransformedVertex[mSurfaces[i].vertexes[1]].y;

			/* calculate the z term in the cross product of these two vectors. If it's positive, it's facing the camera */
			int cross_prod = (ux * vy) - (vx * uy);

			if (cross_prod <= 0)
			{
				continue;
			}

			//			DEBUG.p("ux " + ux + " uy " + uy + " vx " + vx + " vy " + vy);
			//			DEBUG.p("cross_prod " + cross_prod);
			int color = cross_prod / (5000 / 256);

			if (mSurfaces[i].texture == null)
			{
				inPen.setColor(color, color, color);
	
				for (int vertex = 0; vertex < mSurfaces[i].vertexes.length;
					vertex++)
				{
					mSurfaces[i].x[vertex]     = mTransformedVertex[mSurfaces[i].vertexes[vertex]].x;
					mSurfaces[i].y[vertex]     = mTransformedVertex[mSurfaces[i].vertexes[vertex]].y;
				}
	
				inPen.fillPoly(mSurfaces[i].x, mSurfaces[i].y);

			/*
				for(int vertex = 1; vertex < mSurfaces[i].vertexes.length; vertex++) 
				{
					pointA = mTransformedVertex[mSurfaces[i].vertexes[vertex-1]];
					pointB = mTransformedVertex[mSurfaces[i].vertexes[vertex]];
					inPen.drawLine(pointA.x, pointA.y, pointB.x, pointB.y);
				}
				pointA = mTransformedVertex[mSurfaces[i].vertexes[mSurfaces[i].vertexes.length-1]];
				pointB = mTransformedVertex[mSurfaces[i].vertexes[0]];
				inPen.drawLine(pointA.x, pointA.y, pointB.x, pointB.y);
			*/
			}
			else
			{
				for (int vertex = 0; vertex < mSurfaces[i].vertexes.length;
					vertex++)
				{
					mSurfaces[i].x[vertex]     = mTransformedVertex[mSurfaces[i].vertexes[vertex]].x;
					mSurfaces[i].y[vertex]     = mTransformedVertex[mSurfaces[i].vertexes[vertex]].y;
					//System.out.println("vert " + vertex + "=" + mSurfaces[i].x[vertex] + "," + mSurfaces[i].y[vertex]);
				}

				int tx = mSurfaces[i].x[0];
				int ty = mSurfaces[i].y[0];
				
				mSurfaces[i].x[0] = mSurfaces[i].x[2];
				mSurfaces[i].y[0] = mSurfaces[i].y[2];
				mSurfaces[i].x[2] = tx;
				mSurfaces[i].y[2] = ty;

				inPen.fillTexturedPoly(mSurfaces[i].texture, mSurfaces[i].x, mSurfaces[i].y,
								mSurfaces[i].u, mSurfaces[i].v);
			}
		}
	}

	//~ Inner Classes ----------------------------------------------------------

	class lineSegment
	{
		//~ Instance fields ----------------------------------------------------

		public int startVertex;
		public int endVertex;

		//~ Constructors -------------------------------------------------------

		public lineSegment()
		{
		}
	}

	class surface
	{
		//~ Instance fields ----------------------------------------------------

		public int[]  vertexes;
		public int[]  x;
		public int[]  y;

		public Bitmap texture;
		public int[]  u;
		public int[]  v;

		//~ Constructors -------------------------------------------------------

		public surface()
		{
		}
	}
}
