import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;
import java.util.Enumeration;

public class Test extends JFrame {
        JMenuBar menuBar1 = new JMenuBar();
        JMenu menuFile = new JMenu();
        JMenuItem menuChangeColor = new JMenuItem();
        JMenuItem menuFileExit = new JMenuItem();
        JLabel statusBar = new JLabel();
        BorderLayout borderLayout1 = new BorderLayout();
	
	static final int    POST_AWT_CHANGE = 1;

        static final float AREA_SIZE = (1.0f);
        static final int GEOM_NUM = (100);

        OnCanvas3D      canvas;
        VirtualUniverse universe;
        Locale locale;
        BranchGroup objRoot;
        BranchGroup vpRoot;
        Shape3D shape[] = new Shape3D[GEOM_NUM*GEOM_NUM];
        float[] color = { 0.0f, 1.0f, 0.0f };
        int shape_count=0;
        long change_start = 0;
        long change_end;
	UpdateBehavior 	updateBehavior;

        public Test() {
                JPopupMenu.setDefaultLightWeightPopupEnabled(false);
                enableEvents(AWTEvent.WINDOW_EVENT_MASK);
                try     {
                        jbInit();
                }
                catch(Exception e) {
                        e.printStackTrace();
                }

                canvas = new OnCanvas3D ( SimpleUniverse.getPreferredConfiguration() );
                getContentPane().add(canvas,BorderLayout.CENTER);

                createSceneGraph();
        }

        public void createSceneGraph() {
                /**
                 * Create the object root of the branch graph
                 */
                objRoot = new BranchGroup();
                objRoot.setCapability(BranchGroup.ALLOW_DETACH);

                /*
                 * Create geom data (Shape3D)
                 */
                int i,j;
                float pich;
                float base;
                float x, xx, y, yy;
                float[] vertex = new float[12];
                float[] normal = { 0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f };
                int[] index = { 0, 1, 2,
                                                                                0, 2, 3 };
                pich = AREA_SIZE / GEOM_NUM;

                base = -(AREA_SIZE / 2.0f);
                for(i=0;i<GEOM_NUM;i++){
                        x = base + pich * i;
                        xx = base + pich * (i+1);
                        for(j=0;j<GEOM_NUM;j++){
                                y = base + pich * j;
                                yy = base + pich * (j+1);

                                vertex[0] = x;        vertex[1] = y;        vertex[2] = 0.0f;
                                vertex[3] = xx; vertex[4] = y;      vertex[5] = 0.0f;
                                vertex[6] = xx; vertex[7] = yy; vertex[8] = 0.0f;
                                vertex[9] = x;        vertex[10] = yy; vertex[11] = 0.0f;
                                IndexedTriangleArray geometry =
                                        new IndexedTriangleArray( 4,
                                                GeometryArray.COORDINATES | GeometryArray.NORMALS ,
                                                6);
                                geometry.setCoordinates(0,vertex);
                                geometry.setCoordinateIndices(0, index);
                                geometry.setNormals(0, normal);

                                Material material = new Material();
                                material.setCapability(Material.ALLOW_COMPONENT_READ);
                                material.setCapability(Material.ALLOW_COMPONENT_WRITE);
                                material.setDiffuseColor( new Color3f(color) );
                                material.setShininess(128.0f);

                                Appearance appearance = new Appearance();
                                appearance.setCapability(Appearance.ALLOW_MATERIAL_READ);
                                appearance.setMaterial(material);

                                shape[shape_count] = new Shape3D(geometry,appearance);
                                shape[shape_count].setCapability(Shape3D.ALLOW_APPEARANCE_READ);
                                objRoot.addChild(shape[shape_count++]);
                        }
                }
		BoundingSphere bounds = new BoundingSphere(
			new Point3d(0,0,0),
			((BoundingSphere)objRoot.getBounds()).getRadius());
                DirectionalLight dirLight =new DirectionalLight( 
			new Color3f(1.0f, 1.0f, 1.0f),
			new Vector3f(0.0f, -1.0f, -1.0f) );
                dirLight.setInfluencingBounds(bounds);
                dirLight.setEnable(true);
                objRoot.addChild(dirLight);

		/* add the update behavior */
		updateBehavior = new UpdateBehavior();
		updateBehavior.setSchedulingBounds(bounds);
		objRoot.addChild(updateBehavior);

                /*
                 * Create Virtual Universe
                 */
                universe = new VirtualUniverse();
                locale = new Locale(universe);

                vpRoot = new BranchGroup();
                vpRoot.setCapability(BranchGroup.ALLOW_DETACH);

                /*
                 * Create Viewing environment
                 */
                ViewPlatform platform = new ViewPlatform();
                View view = new View();
                view.setPhysicalBody(new PhysicalBody());
                view.setPhysicalEnvironment(new PhysicalEnvironment());
                view.setBackClipDistance(10.0) ;
                view.attachViewPlatform(platform);
                view.addCanvas3D(canvas);

                /*
                 * Set viewing matrix and add to viewport root
                 */
                Transform3D trans = new Transform3D();
                trans.setTranslation(new Vector3d(0.0, 0.0, ((BoundingSphere)objRoot.getBounds()).getRadius()*3.0) );
                TransformGroup transGroup = new TransformGroup(trans);
                transGroup.addChild(platform);

                vpRoot.addChild(transGroup);

                /*
                 * Add object root and view to locale
                 */
                locale.addBranchGraph(objRoot);
                locale.addBranchGraph(vpRoot);

                System.out.println("J3D priority ("+ Thread.MIN_PRIORITY + " - " + Thread.MAX_PRIORITY     + ") =" +universe.getJ3DThreadPriority() );
                //universe.setJ3DThreadPriority(3);
        }

        /**
         * Window menu set up
         */
        private void jbInit() throws Exception  {
                this.getContentPane().setLayout(borderLayout1);
                this.setSize(new Dimension(400, 300));
                this.setTitle("Color change test");
                statusBar.setText(" ");
                menuFile.setText("File");
                menuChangeColor.setText("Change color");
                menuChangeColor.addActionListener(new ActionListener()  {

                        public void actionPerformed(ActionEvent e) {
                                changeColor_actionPerformed(e);
                        }
                });
                menuFile.add(menuChangeColor);

                menuFileExit.setText("exit");
                menuFileExit.addActionListener(new ActionListener()     {

                        public void actionPerformed(ActionEvent e) {
                                fileExit_actionPerformed(e);
                        }
                });

                menuFile.add(menuFileExit);
                menuBar1.add(menuFile);
                this.setJMenuBar(menuBar1);
                this.getContentPane().add(statusBar, BorderLayout.SOUTH);
        }

        /**
         * Change color action
         */
        public void changeColor_actionPerformed(ActionEvent e) {
		updateBehavior.postId(POST_AWT_CHANGE);
	}

	private class UpdateBehavior extends Behavior {
	    WakeupCriterion criterion[] = {
		    new WakeupOnBehaviorPost(null, POST_AWT_CHANGE)
		    };
	    WakeupCondition conditions = new WakeupOr( criterion );

	    public void initialize() {
		wakeupOn(conditions);
	    }

	    public void processStimulus( Enumeration criteria) {
		// Do the update
		update();

		wakeupOn(conditions);
	    }
	}

	void update() {
                int i;
                if( color[1] == 1.0f ){
                        color[1] = 0.0f; color[2] = 1.0f;
                }
                else {
                        color[1] = 1.0f; color[2] = 0.0f;
                }
                change_start = System.currentTimeMillis();
                for(i=0;i<shape_count;i++){
                        shape[i].getAppearance().getMaterial().setDiffuseColor(
			color[0],color[1],color[2]);
                }
        }


        /**
         * Exit action
         */
        public void fileExit_actionPerformed(ActionEvent e) {
                System.exit(0);
        }

        /**
         * System close
         */
        protected void processWindowEvent(WindowEvent e) {
                super.processWindowEvent(e);
                if(e.getID() == WindowEvent.WINDOW_CLOSING) {
                        fileExit_actionPerformed(null);
                }
        }


        /**
         * Canvas3D
         */
        public class OnCanvas3D extends Canvas3D {
                long    pre_render;
                long    post_render;
                long    post_swap;

                public OnCanvas3D( GraphicsConfiguration gc ) {
                        super(gc);
                }

                public void preRender() {
                        pre_render = System.currentTimeMillis();
                }

                public void postRender() {
                        post_render = System.currentTimeMillis();
                }

                public void postSwap() {
                        post_swap = System.currentTimeMillis();
                        System.out.println("Render msec = ( "+ pre_render +" - "+post_swap+" ) === "+ (post_swap-pre_render) );
                        if( change_start != 0 ){
                                change_end = post_swap;
                                System.out.println("Color change msec = ( "+ change_start +" - "+change_end+" ) === "+ (change_end-change_start) );
                                change_start = 0;
                        }
                }
        }

        /**
         * Main routine
         */
        public static void main(String[] args) {
                try     {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                }
                catch(Exception e) {
                }
                Test frame = new Test();
                frame.setVisible(true);
        }
}
