//<pre>
import java.awt.*;
import java.awt.event.*;

public class Animation3D extends BufferedApplet
{
   	int width = 0, height = 0;
   	Color shapeColor = Color.red;
   	
   	double parallax = 0.42;					//  separation between cubes
   	double offset = 0.01;
   	double c = 0.2;							//	size of cube, zoom
   	double zoom = 1.0;
   							
	double a[] = {0,0,0}, b[] = {0,0,0};

	Shape3D myShapes[];
	int numShapes = 0;
	
	Matrix3D windowTransformation;							// 	right eye perspective
	Matrix3D vMatrix;							//	"orientation matrix"
	Matrix3D vOldMatrix;
	
	Shape3D ground, wheel, tube, seat, statue;

	double focalLength = 10.0;
	
	boolean spin = false;						// 	controls continuous spinning -- "on/off"
		
	public Animation3D () {
	
		double time = System.currentTimeMillis() / 1000.0;

		vMatrix = new Matrix3D();
		vOldMatrix = new Matrix3D();
		
		windowTransformation = new Matrix3D();
		
		
		
		vMatrix.identity();
		vOldMatrix.identity();
		windowTransformation.identity();
//		windowTransformation.rotateX(-1*Math.PI/4);
//		windowTransformation.rotateZ(Math.PI/4);
		
		myShapes = new Shape3D[10];
		myShapes[numShapes++] = new Plane(20);

		myShapes[numShapes++] = new Cylinder(20);
		myShapes[numShapes++] = new Cylinder(20);
		myShapes[numShapes++] = new Sphere(20);
		myShapes[numShapes++] = new Sphere(30);

		ground = myShapes[0];
		wheel = myShapes[1];
		tube = myShapes[2];
		seat = myShapes[3];
		statue = myShapes[4];
	}

	public void render(Graphics g) {

		int theta = 0;
		double time = System.currentTimeMillis() / 1000.0;

		if (width == 0) {
			width  = bounds().width;
			height = bounds().height;
		}

		g.setColor(Color.black);
		g.fillRect(0, 0, width, height);
		
		animate();
		
		g.setColor(Color.yellow);
		
		int v1, v2;
		int faceSize;
		Shape3D s;
		
		// cycle through array of shapes and render each one
		// do "viewport" transformation along the way
		for (int k = 0; k < numShapes; k++) {
			s = myShapes[k];
			for (int i = 0; i < s.faces.length; i++) {
				faceSize = s.faces[i].length;
				for (int j = 0; j < faceSize; j++) {
					v1 = s.faces[i][j];
					v2 = s.faces[i][(j+1) % faceSize];
					
					g.drawLine(	x(s.transformedVertices[v1][0],s.transformedVertices[v1][2]),
								y(s.transformedVertices[v1][1],s.transformedVertices[v1][2]),
								x(s.transformedVertices[v2][0],s.transformedVertices[v2][2]),
								y(s.transformedVertices[v2][1],s.transformedVertices[v2][2]));
// 					g.drawOval(x(s.transformedVertices[v1][0]),y(s.transformedVertices[v1][1]),1,1);

				}      		
			}
		}
		
		animating = true;

	}
	
	

	public void animate( ) {

		double groundLevel = 2.0;
		double wheelRadius = 1.0;
		double pathRadius = 5.0;
		double spinSpeed = .001;
		double bikeHeight = 1.3;
		double tubeThickness = 0.05;
		double topTubeLength = 1.5;

		double time = System.currentTimeMillis() / 1000.0;
//		double time = 0.0;
		windowTransformation.copy(vMatrix);
		windowTransformation.translate(0.0,0.0,-1.0);
		windowTransformation.scale(0.5,0.5,0.5);
		Matrix3D temp = new Matrix3D();
		Matrix3D t = new Matrix3D();
		
		// ground
		temp.identity();	
		t.identity();
		t.copy(windowTransformation);
		t.translate(0.0,2.0,0.0);
		t.rotateX(Math.PI/2);
		t.scale(pathRadius,pathRadius,pathRadius);
		ground.update(t);

		// wheel
		t.copy(windowTransformation);
		t.translate(0.0,groundLevel-wheelRadius,0.0);
		t.rotateY(time);
		t.translate(0.0,0.0,pathRadius);
		t.rotateZ(0-time);
		t.scale(wheelRadius,wheelRadius,0.05);
		wheel.update(t);

		// tube
		t.copy(windowTransformation);
		t.translate(0.0,groundLevel-wheelRadius,0.0);
		t.rotateY(time);
		t.rotateZ(Math.PI/2);
		t.translate(1.0,0.0,pathRadius);
		t.scale(1.0,tubeThickness,tubeThickness);
		tube.update(t);

		// seat
		t.copy(windowTransformation);
		t.translate(0.0,groundLevel-wheelRadius,0.0);
		t.rotateY(time);
		t.rotateZ(Math.PI/2);
		t.translate(2.0,0.0,pathRadius);
		t.scale(0.1,0.5,0.1);
		seat.update(t);

		// statue
		t.copy(windowTransformation);
		t.translate(0.0,groundLevel-2.0,0.0);
		t.scale(2.0,2.0,2.0);
		statue.update(t);


// 		for (int i = 0; i < numShapes; i++) {
// 			Shape3D s = myShapes[i];
// 			s.update(windowTransformation);
// 		}
		
	}

	// viewport transformations
   	int x(double t, double z) {  // CONVERT X COORDINATE TO SCREEN PIXELS
   		t = focalLength * t / (focalLength - z);
   		return width/2 + (int)(t*width/4); 
   	}
   	
   	int y(double t, double z) { // CONVERT Y COORDINATE TO SCREEN PIXELS
   		t = focalLength * t / (focalLength - z);
 		return height/2 + (int)(t*width/4); 
 
 
 } 

	// not used
   	public boolean mouseUp(Event e, int x, int y) {
		
		vOldMatrix.copy(vMatrix);
		spin = (spin != true);									// toggle spin on/off
		damage = true;
      	return true;
   	}
   
    // move camera
   	public boolean mouseDrag(Event e, int x, int y) {
		Matrix3D temp = new Matrix3D();
		
		vMatrix.copy(vOldMatrix);
		temp.yRotationMatrix(Math.PI * (x + width / 2) / (width / 2));
		vMatrix.multiply(temp);
		temp.xRotationMatrix(Math.PI * (y + height / 2) / (height / 2));
 		vMatrix.multiply(temp);
		
	 	damage = true;
	 	return true;
   	}
   	
   	// handle key events
   	public boolean keyDown(Event e, int key) {
      switch (key) {
      	case '\u03EC': zoom += 0.05; break;						// UP ARROW -- zoom in
      	case '\u03ED': zoom -= 0.05; break;						// DOWN ARROW -- zoom out
      	case '\u03EE': parallax = parallax - 0.02;	break;		// LEFT ARROW -- decrease parallax
      	case '\u03EF': parallax = parallax + 0.02;	break;		// RIGHT ARROW -- increase parallax
      	case '.': offset = offset - 0.001;	break;				// RIGHT ARROW -- increase parallax
      	case '/': offset = offset + 0.001;	break;				// RIGHT ARROW -- increase parallax

		}
		System.out.println("scale is " + c);
		System.out.println("parallax is " + parallax);
		System.out.println("offset is " + offset);

		return true;
	}

}
