/*Created by Jennifer Mc Gann.
Some code is based on examples from: 
Daniel Selman (www.manning.com) and 
Sun Microsystems (java.sun.com)*/

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import javax.media.j3d.*;
import javax.media.j3d.VirtualUniverse;
import javax.vecmath.*;
import java.io.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.behaviors.picking.*;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.ParsingErrorException;
import com.sun.j3d.loaders.IncorrectFormatException;
import ncsa.j3d.loaders.ModelLoader;
import ncsa.j3d.loaders.Loader_WRL;
import ncsa.j3d.loaders.Loader_IOB;
import ncsa.j3d.loaders.*;


public class StarchTest extends Applet implements ActionListener {

  	protected Button exitButton = new Button("Exit");

    private SimpleUniverse u = null;
    
    private String table = "Models/table.wrl";
    
    private String yellow_starch_bottle = "Models/bottle_yellow_starch.wrl";
    private String empty_bottle = "Models/bottle_empty.wrl";
    
    private String empty_dropper = "Models/dropper_empty.wrl";	
	private String yellow_starch_dropper = "Models/dropper_yellow_starch.wrl";
	private String brown_iodine_dropper = "Models/dropper_brown_iodine.wrl";
	
	private String green_iodine_flask = "Models/flask.iob";
	
	private String test_tube_rack = "Models/test_tube_rack.wrl";
	
	private String empty_test_tube = "Models/test_tube_empty.wrl";
	private String yellow_starch_test_tube = "Models/test_tube_yellow_starch.wrl";
	
	private Switch bottleSwitch;
	private Switch dropperSwitch;		
	private Switch tubeSwitch;

	
    //create the content side of the scene graph
    public BranchGroup createSceneGraph(Canvas3D canvas) {

	
		//Create the root of the scenegraph
		BranchGroup sceneRoot = new BranchGroup();
	
		//Create the bounding leaf nodes 
		BoundingSphere bounds = new BoundingSphere( new Point3d( 0.0,0.0,0.0 ), 100.0 );
		
		/*BoundingBox bottlebounds = new BoundingBox( new Point3d(-0.9, 6.0, -0.9),new Point3d(0.9, 6.5, 0.9));
		BoundingBox tubebounds = new BoundingBox( new Point3d(-0.6, 3.1, -0.6),new Point3d(-0.6, 3.6, 0.6));
		BoundingBox flaskbounds = new BoundingBox( new Point3d(-0.5, 5.8, -0.5),new Point3d(0.5, 6.0, 0.5));*/
		
		
		// Attach picking behavior utlities to the scene root.
		// They will wake up when user manipulates a scene node.
		PickRotateBehavior rotbehaviour = new PickRotateBehavior(sceneRoot, canvas, bounds);
		sceneRoot.addChild(rotbehaviour);

		PickZoomBehavior zoombehaviour = new PickZoomBehavior(sceneRoot, canvas, bounds);
		sceneRoot.addChild(zoombehaviour);

		PickTranslateBehavior transbehaviour = new PickTranslateBehavior(sceneRoot, canvas, bounds);
		sceneRoot.addChild(transbehaviour);
	
	
		//NEW TRANSFORMGROUP FOR TABLE.WRL
		TransformGroup tableTrans = new TransformGroup();
		
		//enable modification of transform at runtime
		tableTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tableTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
	
		//set the position and scale of the table.
		Transform3D tabletr = new Transform3D();
    	tableTrans.getTransform(tabletr);
    	tabletr.setTranslation(new Vector3d(0.0, -10.0, -48.0)); 	
    	tabletr.setScale(0.5);
    	tableTrans.setTransform(tabletr);
	
		// load the table.wrl file
		Scene tablescene = null;
		

		// read in the geometry information from the table data file
		ModelLoader  tableFileloader = new ModelLoader();

			try
			{
				tablescene = tableFileloader.load(table);
			}
			catch (Exception e)
			{
				tablescene = null;
				System.err.println(e);
			}

			if(tablescene == null)
				System.exit(1);
	
		
		//apply the transform to the table file and attach this to the root of the scene.
		tableTrans.addChild(tablescene.getSceneGroup());
		sceneRoot.addChild(tableTrans);	
	
	
	
		
		
		
		
		
		
		
		
		
		
		
		
		//NEW TRANSFORMGROUP FOR BOTTLE.WRL
		TransformGroup bottleTrans = new TransformGroup();
		
		//enable modification of transform at runtime
		bottleTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		bottleTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		
		//enable the picking of the bottle
		bottleTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		
		//set the collision bounds of the bottle. Allow them to be read and written
		//to at runtime.
		bottleTrans.setCapability(TransformGroup.ALLOW_COLLISION_BOUNDS_READ);
		bottleTrans.setCapability(TransformGroup.ALLOW_COLLISION_BOUNDS_WRITE);

		//create a switch node for the bottle and enable it to be written to.
		bottleSwitch = new Switch(0);
	    bottleSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
		
		//create a bounds for the bottle to be used for collsion detection.
		BoundingBox bottlebounds = new BoundingBox( new Point3d(-0.9, 6.0, -0.9),new Point3d(0.9, 6.5, 0.9));
		
		
		//set the position and scale of the bottle.
		Transform3D bottletr = new Transform3D();
    	bottleTrans.getTransform(bottletr);
    	bottletr.rotX(Math.PI*3/2.0d);
    	bottletr.setTranslation(new Vector3d(-10.0, 1.0, -48.0)); 	
    	bottletr.setScale(0.4);
    	bottleTrans.setTransform(bottletr);

		// load the bottle object files.
		Scene bottlescene = null;
		Scene bottlescene1 = null;
		

		// read in the geometry information from the yellow stach bottle data file
		ModelLoader  yellow_bottleFileloader = new ModelLoader();

			try
			{
				bottlescene = yellow_bottleFileloader.load(yellow_starch_bottle);
			}
			catch (Exception e)
			{
				bottlescene = null;
				System.err.println(e);
			}

			if(bottlescene == null)
				System.exit(1);

		
		
		// read in the geometry information from the empty bottle data file
		ModelLoader  empty_bottleFileloader = new ModelLoader();

			try
			{
				bottlescene1 = empty_bottleFileloader.load(empty_bottle);
			}
			catch (Exception e)
			{
				bottlescene1 = null;
				System.err.println(e);
			}

			if(bottlescene1 == null)
				System.exit(1);
		
		
		//add the loaded bottle files to the switch
		bottleSwitch.addChild(bottlescene.getSceneGroup());
		bottleSwitch.addChild(bottlescene1.getSceneGroup());
		
		
		
		
		
		//attach the bottlebounds and bottleSwitch to the transform and attach the transform to
		//the scene root
		bottleTrans.setBounds(bottlebounds);
		bottleTrans.addChild(bottleSwitch);
		sceneRoot.addChild(bottleTrans);
	
		
		
		
		
		
		
		// NEW TRANSFORM GROUP FOR MYDROPPER1.WRL
		TransformGroup dropperTrans = new TransformGroup();
		
		//enable modification of transform at runtime
		dropperTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		dropperTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		
		//enable the picking of the dropper 
		dropperTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		
		//create a switch node for the dropper and enable it to be written to.
		dropperSwitch = new Switch(0);
	    dropperSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
	
		//set the position and scale of the dropper.
		Transform3D droppertr = new Transform3D();
    	dropperTrans.getTransform(droppertr);
    	droppertr.rotZ(Math.PI*3/4.0d);
    	droppertr.setScale( 0.7);
    	droppertr.setTranslation(new Vector3d(0.5, 7.0, -48.0)); 
    	dropperTrans.setTransform(droppertr);

		// load the dropper object files
		Scene dropperscene = null;
		Scene dropperscene1 = null;
		Scene dropperscene2 = null;
		
		
		// read in the geometry information from the empty dropper data file
		ModelLoader  empty_dropperFileloader = new ModelLoader();

			try
			{
				dropperscene = empty_dropperFileloader.load(empty_dropper);
			}
			catch (Exception e)
			{
				dropperscene = null;
				System.err.println(e);
			}

			if(dropperscene == null)
				System.exit(1);

		
		// read in the geometry information from the yellow starch dropper data file
		ModelLoader  yellow_dropperFileloader = new ModelLoader();

			try
			{
				dropperscene1 = yellow_dropperFileloader.load(yellow_starch_dropper);
			}
			catch (Exception e)
			{
				dropperscene1 = null;
				System.err.println(e);
			}

			if(dropperscene1 == null)
				System.exit(1);
		
		
		
		// read in the geometry information from the brown iodine dropper data file
		ModelLoader  brown_dropperFileloader = new ModelLoader();

			try
			{
				dropperscene2 = brown_dropperFileloader.load(brown_iodine_dropper);
			}
			catch (Exception e)
			{
				dropperscene2 = null;
				System.err.println( e );
			}

			if(dropperscene2 == null)
				System.exit(1);
		
		//add the loaded dropper files to the switch
		dropperSwitch.addChild(dropperscene.getSceneGroup());
		dropperSwitch.addChild(dropperscene1.getSceneGroup());
		dropperSwitch.addChild(dropperscene2.getSceneGroup());
		
		//attach the switch to the transform and the transform to the scene root
		dropperTrans.addChild(dropperSwitch);
		sceneRoot.addChild(dropperTrans);
		
	
		
		
		
		
		
		
		
		// NEW TRANSFORM GROUP FOR FLASK.IOB
		TransformGroup flaskTrans = new TransformGroup();
		
		//enable modification of transform at runtime
		flaskTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		flaskTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		
		//enable the picking of the flask 
		flaskTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		
		//create a bounds for the flask to be used for collsion detection.
		BoundingBox flaskbounds = new BoundingBox( new Point3d(-0.5, 5.8, -0.5),new Point3d(0.5, 6.0, 0.5));
			
		//set the position and scale of the flask.
		Transform3D flasktr = new Transform3D();
    	flaskTrans.getTransform(flasktr);
    	flasktr.rotX(Math.PI*3/2.0d);
    	flasktr.setScale(0.4);
    	flasktr.setTranslation(new Vector3d(10.0, -3.0, -48.0)); 
    	flaskTrans.setTransform(flasktr);

		// load the flask object file
		Scene flaskscene = null;

		// read in the geometry information from the flask data file
		ModelLoader  flaskFileloader = new ModelLoader();

			try
			{
				flaskscene = flaskFileloader.load(green_iodine_flask);
			}
			catch (Exception e)
			{
				flaskscene = null;
				System.err.println(e);
			}

			if(flaskscene == null)
				System.exit( 1 );

		
		//apply the transform to the flask file, attach the bounds to the flask
		//and attach the flask transform to the root of the scene.
		flaskTrans.addChild(flaskscene.getSceneGroup());
		flaskTrans.setBounds(flaskbounds);
		sceneRoot.addChild(flaskTrans);
		
		
	
	
		
		
		// NEW TRANSFORM GROUP FOR TEST_TUBE_RACK.WRL
		TransformGroup rackTrans = new TransformGroup( );
		
		//enable modification of transform at runtime
		rackTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		rackTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
	
		//set the position and scale of the rack.
		Transform3D racktr = new Transform3D();
    	rackTrans.getTransform(racktr);
    	racktr.setScale(0.1);
    	racktr.setTranslation(new Vector3d(0.0, -5.0, -48.0)); 
    	rackTrans.setTransform(racktr);

		// load the test tube rack object file
		Scene rackscene = null;
		
		// read in the geometry information from the test tube rack data file
		ModelLoader  rackFileloader = new ModelLoader();

			try
			{
				rackscene = rackFileloader.load(test_tube_rack);
			}
			catch (Exception e)
			{
				rackscene = null;
				System.err.println(e);
			}

			if(rackscene == null)
				System.exit(1);
		
		//apply the transform to the test tube rack file 
		//and attach this to the root of the scene.
		rackTrans.addChild(rackscene.getSceneGroup());
		sceneRoot.addChild(rackTrans);
		
		
	
	
	
	
	
	
	
	
		// NEW TRANSFORM GROUP FOR TEST_TUBE.WRL
		TransformGroup tubeTrans = new TransformGroup( );
		
		//enable modification of transform at runtime
		tubeTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tubeTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		
		//enable the picking of the test tube 
		tubeTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		
		//set the collision bounds of the test tube. Allow them to be read and 
		//written to at runtime.
		tubeTrans.setCapability(TransformGroup.ALLOW_COLLISION_BOUNDS_READ);
		tubeTrans.setCapability(TransformGroup.ALLOW_COLLISION_BOUNDS_WRITE);
		
		//create a switch node for the test tube and enable it to be written to.
		tubeSwitch = new Switch(0);
	    tubeSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
		
		//create a bounds for the test tube to be used for collsion detection.
		BoundingBox tubebounds = new BoundingBox( new Point3d(-0.6, 3.1, -0.6),new Point3d(-0.6, 3.6, 0.6));
		
		//set the position and scale of the test tube.
		Transform3D tubetr = new Transform3D();
    	tubeTrans.getTransform(tubetr);
    	tubetr.setScale( 0.04);
    	tubetr.setTranslation(new Vector3d(0.0, -2.0, -48.0)); 
    	tubeTrans.setTransform(tubetr);

		// load the test tube object files.
		Scene tubescene = null;
		Scene tubescene1 = null;
		

		// read in the geometry information from the empty tube data file
		ModelLoader  empty_tubeFileloader = new ModelLoader();

			try
			{
				tubescene = empty_tubeFileloader.load(empty_test_tube);
			}
			catch (Exception e)
			{
				tubescene = null;
				System.err.println(e);
			}

			if(tubescene == null)
				System.exit(1);
		
		// read in the geometry information from the yellow tube data file
		ModelLoader  yellow_tubeFileloader = new ModelLoader();

			try
			{
				tubescene1 = yellow_tubeFileloader.load(yellow_starch_test_tube);
			}
			catch (Exception e)
			{
				tubescene1 = null;
				System.err.println(e);
			}

			if(tubescene1 == null)
				System.exit(1);
		
		//add the loaded test tube files to the switch
		tubeSwitch.addChild(tubescene.getSceneGroup());
		tubeSwitch.addChild(tubescene1.getSceneGroup());
		
		//attach the bounds to the test tube, attach the tube switch to the transform
		//and attach the tube transform to the scene root
		tubeTrans.setBounds(tubebounds);
		tubeTrans.addChild(tubeSwitch);
		sceneRoot.addChild(tubeTrans);
		
		
		
		
		
		// Add global lights to the content branch of the scenegraph.
		//First define the colours of the light
		Color3f alCol = new Color3f( 50.2f, 50.2f, 50.2f );
    	Color3f dlCol = new Color3f( 0.7f, 0.7f, 0.7f );
		Vector3f lDir  = new Vector3f( -1.0f, -1.0f, -1.0f );
		
			
		//Define the ambient light
		AmbientLight aLgt1 = new AmbientLight( alCol );
		aLgt1.setInfluencingBounds( bounds );
		
		//Define the directional light
		DirectionalLight dLgt1 = new DirectionalLight( dlCol, lDir );
		dLgt1.setInfluencingBounds( bounds );

		DirectionalLight dLgt1a = new DirectionalLight();
  		dLgt1a.setColor(new Color3f(1.0f,1.0f,1.0f));
  		dLgt1a.setDirection(0.30000001192092896f,0.30000001192092896f,-0.699999988079071f);
  		dLgt1a.setInfluencingBounds(new BoundingBox(new Point3d(-70.0, -70.0, -70.0),new Point3d(70.0, 70.0, 70.0)));
  		dLgt1a.setEnable(true);
  
	  	DirectionalLight dLgt1b = new DirectionalLight();
	  	dLgt1b.setColor(new Color3f(1.0f,1.0f,1.0f));
	  	dLgt1b.setDirection(0.30000001192092896f,0.30000001192092896f,-0.699999988079071f);
	  	dLgt1b.setInfluencingBounds(new BoundingBox(new Point3d(-20.0, -20.0, -20.0),new Point3d(20.0, 20.0, 20.0)));
	  	dLgt1b.setEnable(true);
  
	   	DirectionalLight dLgt1c = new DirectionalLight();
	  	dLgt1c.setColor(new Color3f(1.0f,1.0f,1.0f));
	  	dLgt1c.setDirection(0.30000001192092896f,0.30000001192092896f,-0.699999988079071f);
	 	dLgt1c.setInfluencingBounds(new BoundingBox(new Point3d(-20.0, -20.0, -20.0),new Point3d(20.0, 20.0, 20.0)));
	  	dLgt1c.setEnable(true);
  	
		//attach the lights to the scene root
		sceneRoot.addChild(aLgt1);
		sceneRoot.addChild(dLgt1);
		sceneRoot.addChild(dLgt1a);
		sceneRoot.addChild(dLgt1b);
		sceneRoot.addChild(dLgt1c);

	
		//Set up the background
        Color3f bgColor = new Color3f(0.00f, 0.00f, 1.0f);
        Background bgNode = new Background(bgColor);
        bgNode.setApplicationBounds(bounds);
        sceneRoot.addChild(bgNode);

	
		//call the behaviour classes and pass in the various parameters
		BottleBehaviour bb = new BottleBehaviour(bottlescene.getSceneGroup(), bottleSwitch, dropperSwitch, bottlebounds);
		sceneRoot.addChild(bb);
		

		FlaskBehaviour fb = new FlaskBehaviour(flaskscene.getSceneGroup(), dropperSwitch, flaskbounds);
		sceneRoot.addChild(fb);
	
	
		TubeBehaviour tb = new TubeBehaviour(tubescene.getSceneGroup(), tubeSwitch, dropperSwitch, tubebounds);
		sceneRoot.addChild(tb);	
		


	
		return sceneRoot;
    }


    public StarchTest() {

    }

    // Exit the application 
	public void actionPerformed(ActionEvent e) {
        System.exit(0);
	}
    
    
    
    public void init() {
		
		// These are the string arguments given to the NavigationControls
        // constructor.  These are settable parameters.  Look in the 
        // NavigationControls constructor for a complete list.
        String[] args = new String[10];
        args[0] = "printvalues";
        args[1] = "true";
        args[2] = "yscreeninitloc";
        args[3] = "50";
        args[4] = null;

        InputDevice device = new NavigationControls( args );

        // now create the StarchTest Canvas
		setLayout(new BorderLayout());
        GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

        Canvas3D c = new Canvas3D(config);
		add("Center", c);
		//Add the 'exit' button
		exitButton.addActionListener(this);
	    add("South", exitButton);
	    setVisible(true);

		
		// Create a simple scene and attach it to the virtual universe
		BranchGroup scene = createSceneGraph(c);
		u = new SimpleUniverse(c);

        // The InputDevice is initialized 
        device.initialize();

		// Register the NavigationControls device with Java 3D
		u.getViewer().getPhysicalEnvironment().addInputDevice( device );

		//set the location and bounds of the viewing platform 
		TransformGroup viewTrans = u.getViewingPlatform().getViewPlatformTransform();
		SensorBehavior s = new SensorBehavior( viewTrans, device.getSensor(0) );
		s.setSchedulingBounds( new BoundingSphere ( new Point3d(0.0,0.0,0.0), Float.MAX_VALUE ));
		scene.addChild( s );
		u.addBranchGraph(scene);
    }

    public void destroy() {
	u.removeAllLocales();
    }

	//allows StarchTest to be run as an application as well as an applet.
    public static void main(String[] args) {
	new MainFrame(new StarchTest(), 500, 400);
    }
}
