////////////////
//// RopeBridge.java extends World3D
//// Implementation of a rope-bridge easing under its own weight due to gravity
//// The "rope" segments are actually cylinders which have been rotated into place via inverse kinematics

import wonderlab.graphics.world.*;
import wonderlab.graphics.geometry.*;
import wonderlab.graphics.shape.*;
import wonderlab.graphics.renderer.*;
import wonderlab.graphics.camera.*;

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Iterator;


public class RopeBridge extends World3D{

	Shape3D pole1,pole2,pole3,pole4, water;
	Material nodeMaterial, edgeMaterial, poleMaterial, waterMaterial;
	ArrayList edges, poles;
	Lattice lattice;
	boolean showVertices = false;

	/////////////////////////////////// CONSTANTS /////////////////////////////
	static double gNODE_SIZE = 0.1;
	static double gEDGE_DIAMETER = 0.05;
	static short gLATTICE_LENGTH = 10;
	static short gLATTICE_WIDTH = 2;

	////////////////////////////////// CONSTRUCTORS ///////////////////////////	
	public RopeBridge() {
		super();
		viewport = new Viewport(600,400, camera);
		waterMaterial = new Material(0.0,0.0,0.2, .4,.4,.8, 0,0,0,1.0);
		edgeMaterial = new Material(.3,.0,.0, .5,.5,0, 0,0,0,1);
		poleMaterial = new Material(.3,.2,.2, .5,.2,.2, 0,0,0,1);
		add(new Light(0.0, 0.0, 1, 1.0, 1.0, 1.0));
		add(new Light(0.5, 0.5, 0.5, 1.0, 1.0, 1.0));
		camera.moveAbs(new Vector3D(0.0,0.0,0.0,0.0));
		lattice = new Lattice(gLATTICE_LENGTH,gLATTICE_WIDTH);
		lattice.setK(20);
		edges = new ArrayList();
		
		for (int i = 0; i < lattice.edges.size(); i++) {
			Shape3D newShape = new Cylinder(4);
			newShape.setMaterial(edgeMaterial);
			edges.add(newShape);
			shapes.add(newShape);
		}

		pole1 = new Cylinder(10);
		pole1.setMaterial(poleMaterial);
		shapes.add(pole1);
		pole2 = new Cylinder(10);
		pole2.setMaterial(poleMaterial);
		shapes.add(pole2);
		pole3 = new Cylinder(10);
		pole3.setMaterial(poleMaterial);
		shapes.add(pole3);
		pole4 = new Cylinder(10);
		pole4.setMaterial(poleMaterial);
		shapes.add(pole4);
		water = new Plane(2);
		shapes.add(water);
		water.setMaterial(waterMaterial);
		
		mouseDrag = true;
	}
	
	public void initFrame(double time) { // INITIALIZE ONE ANIMATION FRAME
		S.initialize();
		camera.initialize();
		resetScreen();
		if (animate)	{
			lattice.update();
			animate();
		}
		super.initFrame();
    }    	

	public void animate() {
			Vertex node;
			camera.m().translate(-3,0,-10);
			S.m().copy(camera.m());
			S.m().rotateY(delta_theta);
			S.m().rotateX(delta_phi);
			S.m().rotateX(Math.PI/2.5);
			S.m().rotateZ(Math.PI/3.1);
			
			S.push();
				S.m().translate(-0.5,-0.1,1);
				////// do inverse kinematics to rotate cylinders into place between vertices
				Vector3D direction = new Vector3D(0,0,1);
				Vector3D temp = new Vector3D(0,0,1);
				int[] edge;
				Vertex node1, node2;
				for (int i = 0; i < lattice.edges.size(); i++) {
					S.push();
						Vector3D temp2 = temp.asSpherical();
						edge = (int[])lattice.edges.get(i);
						node1 = lattice.get(edge[0]);
						node2 = lattice.get(edge[1]);
						direction = node1.pos().copy();								// get a direction vector between vertices
						direction.subtract(node2.pos());							// turn it into spherical coordinates
						temp2.subtract(direction.asSpherical());					// calculate the difference between the cylinder's canonical "unrotated" position and the needed direction in spherical coordinates
						S.m().translate(0.5*(node1.pos().get(0) + node2.pos().get(0)), 0.5*(node1.pos().get(1) + node2.pos().get(1)), 0.5*(node1.pos().get(2) + node2.pos().get(2)));			// translate center of cylinder to the midpoint between both vertices
						S.m().rotateZ(temp2.get(2));								// rotate in X-Y plane
						S.m().rotateY(temp2.get(1));								// rotate in X-Z plane
						S.m().scale(gEDGE_DIAMETER,gEDGE_DIAMETER,direction.length());
						((Shape3D)edges.get(i)).update(S.m());
					S.pop();
				}
			S.pop();
			
			// first set of poles
			S.push();
				S.m().translate(-.8,-0.3,2);
				S.m().scale(0.1,0.1,4);
				pole1.update(S.m());
			S.pop();
			S.push();
				S.m().translate(-.8,0.8,2);
				S.m().scale(0.1,0.1,4);
				pole2.update(S.m());
			S.pop();

			S.m().translate(8.6,0,0);
			// second set of poles
			S.push();
				S.m().translate(-.8,-0.3,2);
				S.m().scale(0.1,0.1,4);
				pole3.update(S.m());
			S.pop();
			S.push();
				S.m().translate(-.8,0.8,2);
				S.m().scale(0.1,0.1,4);
				pole4.update(S.m());
			S.pop();

			// draw water
			S.push();
				S.m().translate(-5,0,3.9);
				S.m().scale(4,4,4);
				water.update(S.m());
			S.pop();
	}

	///////////////////////////// USER INTERFACE CONTROLS //////////////////////
	// handle key events
   	public boolean keyDown(Event e, int key) {
    	Vector3D dG = new Vector3D(0,0,0.1);
   		Vector3D temp = lattice.gravity();
      switch (key) {
      	// UP ARROW 	-- increase gravity
      	case '\u03EC': 
      		if (temp.get(2) > 0) temp.subtract(dG);
      		break;						
		// DOWN ARROW 	-- decrease gravity
      	case '\u03ED':
      	     if (temp.get(2) < 6) temp.add(dG);
      		break;
      	// LEFT ARROW
      	case '\u03EE':  
      			break;		
      	// RIGHT ARROW
      	case '\u03EF': 	
      			break;		
      	case '\u0020': 	
      			break;
      	case '.': 		
      			break;		
      	case '/': 		
      			break;
      	case 'd':		
      			debug = !debug;
      			showLines = !showLines;
      			break;
      	case 'a':		animate = !animate;
				break;
		case 'v': showVertices = !showVertices;
				break;

		}
		
		return true;
	}
}