import javax.media.j3d.*;
import javax.vecmath.*;
import javax.swing.JFrame;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import com.sun.j3d.utils.geometry.ColorCube;

/**
 * Debug app for Sun re: orbit behaviour invoking BadTransform Exception.
 *
 * @author Josh Richmond
 */
public class BugViewer extends JFrame
{
    private SimpleUniverse su = null;
    private BoundingSphere bounds =  
	new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 400);

    private BranchGroup geomRoot;

    ViewNavigator vn;
    TransformGroup camera;

    private Canvas3D canvas;

    /**
     * Create the GUI
     */
    public BugViewer()
    {
	this.setTitle("Josh's hacked up viewer");

	// Make sure we close the application when the window closes
	addWindowListener(new WindowAdapter() {
	    public void windowClosing(WindowEvent e) {System.exit(0);}
	});
	
	this.getContentPane().setLayout(new BorderLayout());
    }

    /**
     * Launch the GUI. No model loaded
     */
    public void run()
    {
	// Setup and display the 3D world
	launchVisuals();
    }

    /**
     * Create the 3D world and populate it.
     */
    private void launchVisuals()
    {
  	canvas = 
	    new Canvas3D(SimpleUniverse.getPreferredConfiguration());
	this.getContentPane().add(canvas, BorderLayout.CENTER);
    
	// Set up the universe
  	su = new SimpleUniverse(canvas);

	View view = canvas.getView();

	//
	// Add the view navigator 
	// SUN -- 
	ViewingPlatform vp = su.getViewer().getViewingPlatform();
	camera = vp.getViewPlatformTransform();
      	vn = new ViewNavigator(camera);
	vn.setViewMode(ViewNavigator.WORLD_MODE);
	// -- SUN
  	canvas.addMouseListener(vn);
  	canvas.addMouseMotionListener(vn);
	
	// No model, but add the branch group for it anyway
	BranchGroup root = new BranchGroup();
	geomRoot = new BranchGroup();

	// Add a colour cube for debugging
	geomRoot.addChild(new ColorCube());


	// Lights and nominal view
	setNominalView(geomRoot);
	addLights(root);
	
  	geomRoot.setCapability(BranchGroup.ALLOW_BOUNDS_READ); // for fitting
	root.addChild(geomRoot);

	su.addBranchGraph(root);

	// now that we've got some contents, show the GUI
	this.setVisible(true);
	this.setSize(600, 600);
    }

    /**
     * Use the bounds of the branch group to set a nominal view.
     * Views the extent of the branch group, centered on its center.
     * @param bg BranchGroup containing the scene to which to fit the view.
     */
    private void setNominalView(BranchGroup bg)
    {
	// Set the nominal viewing transform (offset in Z)
	BoundingSphere baseBounds = (BoundingSphere) bg.getBounds();
	System.err.println(baseBounds);
	ViewingPlatform vp = su.getViewer().getViewingPlatform();
	View v = su.getViewer().getView();
	double width = baseBounds.getRadius() * 2.0;
    	double fov = v.getFieldOfView();
    	double offset = width / (Math.tan(fov / 2.0)) ;

    	Transform3D oxf = new Transform3D();
    	oxf.setIdentity();

	// Set the center of the view to the center of the object
	Point3d center = new Point3d();
	baseBounds.getCenter(center);
	oxf.setTranslation(new Vector3d(center.x, center.y, offset));

	camera.setTransform(oxf);
    }

    /**
     * Add ambient light and two point sources to the group.
     * @param root BranchGroup to which to add the lights
     */
    private void addLights(BranchGroup root)
    {
	Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f);
	Background bgNode = new Background(bgColor);
	bgNode.setApplicationBounds(bounds);
	root.addChild(bgNode);

	// Colours of the lights
	Color3f alColour   = new Color3f(0.7f, 0.7f, 0.7f);

	// Create the transform group for each light 
	TransformGroup tgAmbLgt = new TransformGroup();

	// Add them to the scene
	root.addChild(tgAmbLgt);

	// Create the ambient light
	AmbientLight aLgt = new AmbientLight(alColour);

	// Set the influencing bounds
	aLgt.setInfluencingBounds(bounds);

	// Add the lights to the scene
	tgAmbLgt.addChild(aLgt);
    }

    /**
     * Usage: VRMLViewer [file.wrl]
     */
    public static void main(String args[])
    {
	VRMLViewer app = new VRMLViewer();
	app.run();
    }
}

