import javax.swing.*;
import javax.media.j3d.*;
import java.awt.GridLayout;
import com.sun.j3d.utils.universe.SimpleUniverse;
import org.web3d.j3d.loaders.VRML97Loader;
import com.sun.j3d.loaders.*;
import javax.vecmath.*;
import java.io.File;

/**
 * Viewer to test texture disappearance.
 * @author Josh Richmond (2002/01/11)
 */
public class IndieViewer extends JFrame
{
    private final double BACKCLIP_DIST = 1000.0;
    private final double FRONTCLIP_DIST = 0.01;

    JPanel[] viewports;
    final int NVIEWS;
    BranchGroup rootNode;
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 500);

    IndieViewer(int views)
    {
	super("VRML Viewer");
	NVIEWS = views;

	createUniverse();

	// Start with N view ports
	setSize(300, 400);
	setDefaultCloseOperation(EXIT_ON_CLOSE);

	viewports = new JPanel[NVIEWS];
	for(int vp = 0; vp < NVIEWS; vp++) {
	    viewports[vp] = new JPanel();
	    viewports[vp].setLayout(new GridLayout(1,1));
	    viewports[vp].add(createCanvas());
	    viewports[vp].setBorder(BorderFactory.createEtchedBorder());
	}
	
	showTiledViewports();

    }

    /**
     * Shows a single selected viewport.
     */
    public void showSingleViewport(int index)
    {
	System.err.println("Single");
	// remove old panels
	getContentPane().removeAll();
	// set layout to tiled
	getContentPane().setLayout(new GridLayout(1,1));
	// Add one back in
	getContentPane().add(viewports[index]);
	validate();
    }

    /**
     * Shows all the viewports at once.
     */
    public void showTiledViewports()
    {
	System.err.println("Tiled");
	// remove old panels
	getContentPane().removeAll();
	// set layout to tiled
	getContentPane().setLayout(new GridLayout(NVIEWS,1));
	// Add them all back in
	for (int vp = 0; vp < viewports.length; vp++)
	    getContentPane().add(viewports[vp]);
	validate();
    }

    /**
     * Create the 3D Canvas for one viewport
     */
    private Canvas3D createCanvas()
    {
	// Create the Java3D canvas
	Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());

	// Set a default body and environment
	PhysicalBody pBody = new PhysicalBody();
	PhysicalEnvironment pEnv = new PhysicalEnvironment();
	ViewPlatform vp = new ViewPlatform();
	vp.setViewAttachPolicy(View.NOMINAL_HEAD);

	// Set up the view
	View view = new View();
	view.addCanvas3D(canvas);
	view.attachViewPlatform(vp);
	view.setPhysicalBody(pBody);
	view.setPhysicalEnvironment(pEnv);
	view.setBackClipDistance(BACKCLIP_DIST);
	view.setFrontClipDistance(FRONTCLIP_DIST);

	// Set the orientation transform
	BranchGroup bg = new BranchGroup();
	TransformGroup tg = new TransformGroup();
	bg.addChild(tg);
	Transform3D offset = new Transform3D();
	offset.set(new Vector3d(0.0, 0.0, 10.0)); 
	tg.setTransform(offset);
	tg.addChild(vp);

	// optimise
	bg.compile();
	rootNode.addChild(bg);

	return canvas;
    }

    /**
     * Create our universe with a background and lights
     */
    protected void createUniverse()
    {
	// Create the universe
	VirtualUniverse universe = new VirtualUniverse();
	Locale locale = new Locale(universe);

	// Create the root branch group
	// This node includes the cameras, axes, etc..
	rootNode = new BranchGroup();
	rootNode.setCapability(BranchGroup.ALLOW_DETACH); // for makeScenLive
	rootNode.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
	rootNode.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
	rootNode.setCapability(BranchGroup.ALLOW_LOCAL_TO_VWORLD_READ);

	// Setup the background 
	Color3f bgColour = new Color3f(0.0f, 0.0f, 0.0f);
    	Background bgNode = new Background(bgColour);
    	bgNode.setApplicationBounds(bounds);
    	rootNode.addChild(bgNode);

	// Add the lights
	addLights(rootNode);

	// Make it live
	locale.addBranchGraph(rootNode);
    }	

    /**
     * Add ambient light and two point sources to the group.
     * @param root BranchGroup to which to add the lights
     */
    private void addLights(BranchGroup root)
    {
	// Colours of the lights
	Color3f lColour1   = new Color3f(0.9f, 0.9f, 0.9f);
	Color3f lColour2   = new Color3f(1.0f, 1.0f, 1.0f); 
	Color3f lColour3   = new Color3f(1.0f, 1.0f, 1.0f); 
	Color3f alColour   = new Color3f(0.7f, 0.7f, 0.7f);

	// Position 1st light at the world origin
	Transform3D t1 = new Transform3D();
	Vector3d posLight1 = new Vector3d(0.0, 0.0, 100.0);
	t1.set(posLight1);

	// Position second light at the SSRMS
	Transform3D t2 = new Transform3D();
	Vector3d posLight2 = new Vector3d(7.0, -35.0, 15.0);
	t2.set(posLight2);

	// Position second light at the SSRMS
	Transform3D t3 = new Transform3D();
	Vector3d posLight3 = new Vector3d(0.0, 35.0, -15.0);
	t3.set(posLight3);

	// Create the transform group for each light 
	TransformGroup tgLight1 = new TransformGroup(t1);
	TransformGroup tgLight2 = new TransformGroup(t2);
	TransformGroup tgLight3 = new TransformGroup(t3);
	TransformGroup tgAmbLgt = new TransformGroup();

	// Add them to the scene
  	root.addChild(tgAmbLgt);
    	root.addChild(tgLight1);
  	root.addChild(tgLight2);
  	root.addChild(tgLight3);

	// Create the ambient light
	AmbientLight aLgt = new AmbientLight(alColour);

	// Create the point lights
	Light lgt1, lgt2, lgt3;
	Point3f lPoint = new Point3f(0.0f, 0.0f, 0.0f);
	Point3f atten1 = new Point3f(0.8f, 0.02f, 0.0f);
	Point3f atten2 = new Point3f(0.3f, 0.03f, 0.001f);
	Point3f atten3 = new Point3f(0.8f, 0.02f, 0.0f);
	lgt1 = new PointLight(lColour1, lPoint, atten1);
	lgt2 = new PointLight(lColour2, lPoint, atten2);
	lgt3 = new PointLight(lColour3, lPoint, atten3);

	// Set the influencing bounds
	aLgt.setInfluencingBounds(bounds);
	lgt1.setInfluencingBounds(bounds);
	lgt2.setInfluencingBounds(bounds);
	lgt3.setInfluencingBounds(bounds);

	// Add the lights to the scene
	tgAmbLgt.addChild(aLgt);
	tgLight1.addChild(lgt1);
	tgLight2.addChild(lgt2);
	tgLight3.addChild(lgt3);
    }

    /**
     * Loads the VRML file.
     */
    public void loadFile(String filename)
    {
	try {
	    Loader theVRMLLoader = new VRML97Loader(Loader.LOAD_ALL);
	    Scene theScene = theVRMLLoader.load(filename);
	    rootNode.addChild(theScene.getSceneGroup());
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    /**
     * Loads and displays any VRML files given as command arguments.
     */
    public static void main(String args[])
    {
	IndieViewer viewer = new IndieViewer(4);
	viewer.setVisible(true);
	if (args.length > 0) {
	    for (int i = 0; i < args.length; i++)
		viewer.loadFile(args[i]);
	}

	try {
	    Thread.sleep(6000); // try changing this value!!
	    viewer.showSingleViewport(0);
	    Thread.sleep(4000);
	    viewer.showTiledViewports();
	} catch (InterruptedException ie){
	}
    }
}
