/*
NCSA Portfolio
Copyright 1997-1998 The Board of Trustees of the University of Illinois
All Rights Reserved 
*/

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;

import javax.media.j3d.*;
import javax.vecmath.*;

import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import com.sun.j3d.loaders.Scene;

import ncsa.j3d.*;
import ncsa.j3d.loaders.*;
import ncsa.j3d.loaders.play.PlayReader;
import ncsa.j3d.loaders.play.PlayWriter;
import ncsa.j3d.ui.tools.*;
import ncsa.j3d.application.*;
import ncsa.j3d.ui.events.*;
import ncsa.j3d.ui.record.*;

/*
 * This is the Display program that is able to record both actions
 * and screen shots.
 */

public class Display implements ActionListener{

        RecordableCanvas3D canvas = null;
        BranchGroup mainScene = null;
        Frame frame = null;
        ButtonPanel buttonPanel = null;
        PickTool pickTool = null;
        ViewTool viewTool = null;
        ModelLoader modelLoader = null;
        TransformGroup vpTrans = null;

        /* 
         * The main program. Usage java Display <filename>.
         */
        static public void main(String[] args){
                Display disp = null;
                if (args.length == 0) {
                        disp = new Display();
                } else if (args[0].equals("-file")){
                        disp = new Display();
                        disp.load(args[1]);
                }
                else
                        System.out.println("Usage: java Display [-file <filename>]");


        }

        /*
         * This is the constructor that takes a file as an argument.
         * @param filename The file to load while constructing this object.
         */
        public Display() {
                Scene modelScene = null;
                
                /*
                 * Try and load a file, if one was passed in.  Note that
                 * we use ModelLoader to load all files.  We don't have to
                 * decide which 3D model loader to use... ModelLoader does
                 * that for us.
                 */
                modelLoader = new ModelLoader();

                /*
                 * Create the main display frame
                 */
                frame = new Frame("Model Display");
                frame.setLayout(new BorderLayout());
                WindowListener l = new WindowAdapter(){
                        public void windowClosing(WindowEvent e) { 
                                System.exit(0);
                        }
                };
                frame.addWindowListener(l);

                /*
                 * Add menu
                 */
                Menu menu = new Menu("View from the given dataset");
                menu.add( register( new MenuItem("Back")));
                menu.add( register( new MenuItem("Front")));
                menu.add( register( new MenuItem("Left")));
                menu.add( register( new MenuItem("Right")));
                menu.add( register( new MenuItem("Top")));
                menu.add( register( new MenuItem("Bottom")));

                MenuBar bar = new MenuBar();
                bar.add(menu);

                frame.setMenuBar(bar);

                /*
                 * Create a canvas we can record jpeg frames from
                 */

        GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();

                template.setStereo(template.PREFERRED);

        // Get the GraphicsConfiguration that best fits our needs.
        GraphicsConfiguration gcfg =
                GraphicsEnvironment.getLocalGraphicsEnvironment().
                getDefaultScreenDevice().getBestConfiguration(template);

                canvas = new RecordableCanvas3D(gcfg);

                mainScene = createSceneGraph();
                if (modelScene != null)
                        addGroup(modelScene);

                /*
                 * Create a simple universe; set the back clip plane
                 */
                SimpleUniverse u = new SimpleUniverse(canvas);

                View view = u.getViewer().getView();
                view.getPhysicalEnvironment().setSensorCount(100);
                view.setBackClipDistance(100000f);

                /*
                 * Set initial transform for View to 10 down the Z axis
                 */
                ViewingPlatform vp = u.getViewingPlatform();
                vpTrans = vp.getViewPlatformTransform();
                Transform3D transform = new Transform3D();
                Vector3d translate = new Vector3d(0, 0, 40);
                transform.setTranslation(translate);
                vpTrans.setTransform(transform);

                /*
                 * Construct and initialize the tool manager
                 */
                PortfolioApplication portApp = new PortfolioApplication();
                portApp.configure(canvas,mainScene,vpTrans);

                /*
                 * This registers the view transform with the PlayReader
                 * so that it can modify the view point during play back.
                 */
                //PlayReader.addView("RecordableViewTool0",vpTrans);

                u.addBranchGraph(mainScene);

                /*
                 * Create a second frame for the display controls
                 */
                Frame controller = new Frame("Display Control");
                buttonPanel = new ButtonPanel(this);
                buttonPanel.setSize(buttonPanel.getPreferredSize());
                controller.add("Center", buttonPanel);
                controller.setSize(buttonPanel.getPreferredSize());
                controller.setMenuBar(new DisplayMenuBar(this));
                controller.addWindowListener(l);
                controller.setSize(300,300);
                controller.setVisible(true);

                canvas.setSize(300,300);
                frame.add("Center", canvas);

                frame.setSize(frame.getPreferredSize());
                frame.setVisible(true);
        }

        /*
         * This is the method where we get notified when 
         * a MenuItem is selected.
         * @param event The ActionEvent.
         */
        public void actionPerformed(ActionEvent event){
          Transform3D transform = new Transform3D();
          Vector3d translate = new Vector3d();
          String command = event.getActionCommand();
          int offset = 40;

          if(command.equals("Back")){
                translate = new Vector3d(0,0,offset);
          }else if(command.equals("Front")){
                translate = new Vector3d(0,0,-offset);
                transform.rotY( (Math.PI/180) * 180);
          }else if(command.equals("Left")){
                translate = new Vector3d(-offset,0,0);
                transform.rotY( (Math.PI/180) * -90);
          }else if(command.equals("Right")){
                translate = new Vector3d(offset,0,0);
                transform.rotY( (Math.PI/180) * 90);
          }else if(command.equals("Top")){
                translate = new Vector3d(0,offset,0);
                transform.rotX( (Math.PI/180) * -90);
          }else if(command.equals("Bottom")){
                translate = new Vector3d(0,-offset,0);
                transform.rotX( (Math.PI/180) * 90);
          }
          
          transform.setTranslation(translate);
          vpTrans.setTransform(transform);
        }

        /*
         * This is a convience function that allows the menuitem 
         * to be registered to report to this class.
         * @param i The MenuItem.
         */
        protected MenuItem register(MenuItem i){
          i.addActionListener(this);
          return i;
        }

        /*
         * The method that knows how to load a file given a file name.
         * @param filename The name of the file to load.
         */
        public void load(String filename) {
                /*
                 * This notifies the PlayWriter that a file is 
                 * being loaded. This does nothing if you are not
                 * currently recording the actions. For this to 
                 * work properly a second call needs to be done
                 * that associates this file with a name of a group.
                 * This makes a logical connection between the geometry
                 * stored in a file and a group name, so that group
                 * (file geometry) can later be transformed.
                 */
                PlayWriter.loadFile(filename);
                Scene scene = null;
                try {
                        scene = modelLoader.load(filename);

                        if (scene != null) {
                                addGroup(scene);
                        }
                } catch (FileNotFoundException fnfe) {
                        System.out.println(filename + " File not found.");
                        System.out.println("Please specify a different file.");
                }

        }

        /*
         * The method that takes a URL and loads it.
         * @param URLname The name of the URL to load.
         */
        public void load(URL URLname) {

                File fl = new File(URLname.getFile());
                PlayWriter.loadFile(fl.getName());
                Scene scene = null;
                try {
                        scene = modelLoader.load(URLname);
                        if (scene != null) {
                                addGroup(scene);
                        }
                } catch (FileNotFoundException fnfe) {
                        System.out.println(URLname + " URL not found.");
                        System.out.println("Please try again.");
                }

        }

        /*
         * The method that gives acces to the RecordableCanvas so that 
         * recording can be turned on and off in other classes.
         * @return The RecordableCanvas3D that is being drawn on.
         */
        public RecordableCanvas3D getRecordableCanvas3D() {
                return canvas;
        }


        /*
         * Used by the button panel to get all the potential tools
         */
        public String[] getToolNames(){
                return ToolManager.instance().getToolNames();
        }

        /*
         * Used by the button panel to switch between tools
         */
        public void setActiveTool(int i){
                ToolManager.instance().setActiveTool(i);
        }       

        /*
         * Add a new group to the scene.
         */
        public void addGroup(Scene scene) {
                BranchGroup sceneGroup = scene.getSceneGroup();
                BranchGroup g = new BranchGroup();
                g.setCapability(Group.ENABLE_PICK_REPORTING);

                Behavior[] behaviors = scene.getBehaviorNodes();
                /*
                 * The PickableTransformGroup is used by the PickTool to
                 * identify which groups are pickable.  You must add the
                 * BranchGroup to the PickableTransformGroup in order for
                 * the PickTool to manipulate it.
                 */
         
                CollidableTransformGroup ptg = new CollidableTransformGroup(ToolManager.instance());

                ptg.addChild(sceneGroup);

                /*
                 * If there are behaviors associated with this scene, add them
                 */ 
                if(behaviors!=null)
                  for(int i=0;i<behaviors.length;i++){
                        ptg.addChild(behaviors[i]);
                  /*
                   * If the behaviors are recordable, set the canvas so that
                   * they can sync when the canvas records.
                   */
                        if(behaviors[i] instanceof NeedsCanvasForRecord)
                          ((NeedsCanvasForRecord)behaviors[i]).setRendezvous(canvas);
                  
                  }
                g.addChild(ptg);
                mainScene.addChild(g);
                /*
                 * This gives a name to the file just loaded. This is 
                 * so that the file is associated with a name of a 
                 * transform group so that all subsequent transforms
                 * move the correct group (file geometry). If you are not
                 * currently recording actions, this is ignored.
                 */
                PlayWriter.nameFile(ptg);
        }

        /*
         * Makes the world
         */
        private BranchGroup createSceneGraph() {
                // Create the root of the branch graph
                BranchGroup group = new BranchGroup();
                group.setCapability(Group.ENABLE_PICK_REPORTING);
        
                BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 2000000.0);
                //Color3f bgColor = new Color3f(0.17f, 0.65f, .92f);//blue
                Color3f bgColor = new Color3f(0.00f, 0.00f, 0.00f);//blck
                Background bg = new Background(bgColor);
                bg.setApplicationBounds(bounds);
                group.addChild(bg);


                group.setCapability(BranchGroup.ALLOW_DETACH);
                group.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
                group.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
                group.setCapability(BranchGroup.ALLOW_CHILDREN_READ);

                Color3f lColor1 = new Color3f(1.0f,1.0f,1.0f);
                Transform3D t = new Transform3D();
                t.set(new Vector3d(0.0, 0.0, 25.0));
                TransformGroup l1Trans = new TransformGroup(t);

                // Create Geometry for point lights
                Appearance defAppearance = new Appearance();


                Point3f point = new Point3f(30,0,20);
                PointLight lgt1 = new PointLight();
                lgt1.setPosition(point);
                lgt1.setColor(lColor1);
                lgt1.setInfluencingBounds(bounds);

                AmbientLight al = new AmbientLight(true,new Color3f(1,1,1));
                al.setInfluencingBounds(bounds);

                l1Trans.addChild(lgt1);
                l1Trans.addChild(al);
                group.addChild(l1Trans);

                return group;
        }


        public Frame getFrame() {
                return frame;
        }
}
