// IO
import java.io.*;
import java.net.*;

// UTIL
import java.util.ArrayList;

// AWT
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.color.ColorSpace;

//SWING
import javax.swing.JFrame;

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

// SUN J3D
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;


/**
 *
 * @author  Michael Nischt - zero@monoid.net
 */
public class Demo implements WindowListener {
    
    private JFrame theFrame;
    private VirtualUniverse virtualU;
    private Locale locale;
    
    private TransformGroup viewTG;
    private TransformGroup[] viewTGs;

    
        
    /** Creates a new instance of Demo */
    public Demo() {
        
        init();
                
        // get scene-content
        BranchGroup scene = createSceneGraph();
        // optimzie
        scene.compile();
        // finally add complete scene-content
        locale.addBranchGraph(scene);
        
        
        //create the Canvas3D that the View will render into.
        //get the graphics capabilities of the system and create
        //the best Canvas3D possible.
        GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D();
        gc3D.setSceneAntialiasing( GraphicsConfigTemplate.PREFERRED );
        GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getBestConfiguration(gc3D);
                        
        
        // Raster for OffScreen endereing
        ImageBlitCanvas3D canvas3D = new ImageBlitCanvas3D(config);        
        
        viewTG = createView(canvas3D);
               
        
        Transform3D tempT3d = new Transform3D();
        
        //viewTG.getTransform(tempT3d);
        //tempT3d.rotX(-Math.PI/32.0);
        //tempT3d.setTranslation(new Vector3f(0,15,90));
        tempT3d.setTranslation(new Vector3f(0,0,350));
        viewTG.setTransform(tempT3d);
        
        com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior key = new com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior(viewTG);
        key.setSchedulingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000.0));
        
        BranchGroup bg = new BranchGroup();
        bg.addChild(key);
        viewTG.addChild(bg);       
        
        
        ImageRenderCanvas3D[] cs = createCanvasArray(canvas3D);


        //tempT3d.setIdentity();
        //tempT3d.rotX(StrictMath.toRadians(-10));
        //tempT3d.setTranslation(new Vector3f(0,0,550));        
        
        viewTGs = new TransformGroup[cs.length];
        for(int i=0; i<cs.length; i++) {
            viewTGs[i] = createView(cs[i]);
            viewTGs[i].setTransform(tempT3d);
            cs[i].start();
        }
            
        
        // Container-Setup
        theFrame = new JFrame();
        
        theFrame.addWindowListener( this );
        theFrame.setTitle( "_box demo 0.10 alpha" );
        theFrame.setSize( 800, 600 );
        theFrame.getContentPane().add("Center", canvas3D);
        
        theFrame.setResizable(false);
        theFrame.setVisible(true);
        theFrame.requestFocus();
        
    }

    
    
    private void init() {
        //create the Universe
        virtualU = new VirtualUniverse();
        
        //create the position for the Locale
        int[] xPos = { 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] yPos = { 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] zPos = { 0, 0, 1, 5, 0, 0, 0, 0 };
        
        
        HiResCoord hiResCoord = new HiResCoord( xPos, yPos, zPos );
        
        //create the Locale and attach to the VirtualUniverse
        locale = new Locale(virtualU, hiResCoord);
    }
    
    
    
    private BranchGroup createSceneGraph() {
        Transform3D tempT3d = new Transform3D();
        
        // Create the root of the branch graph
        BranchGroup root = new BranchGroup();        
        
                
        // Background
        Background bg = new Background(1.0f,1.0f,1.0f);
        bg.setApplicationBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0), 10000.0));
        root.addChild(bg);
        
        
        
        // Lights Setup
        AmbientLight lightA = new AmbientLight();
        lightA.setInfluencingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0),3200.0));
        lightA.setColor(new Color3f(0.2f, 0.2f, 0.25f));
        
        PointLight lightP = new PointLight();
        lightP.setInfluencingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0),3200.0));
        lightP.setColor(new Color3f(0.42f, 0.42f, 0.42f));
        
        tempT3d.setIdentity();
        tempT3d.setTranslation(new Vector3f(0.0f, 50.0f, 0.0f));
        TransformGroup lightPos = new TransformGroup(tempT3d);
        
        lightPos.addChild(lightA);
        lightPos.addChild(lightP);
        lightPos.addChild(new Sphere(1.0f));
        
        BranchGroup lightBG = new BranchGroup();
        lightBG.addChild(lightPos);
        root.addChild(lightBG);
        
        
        Appearance ap = new Appearance();
        Material ma = new Material();
        ma.setAmbientColor(0.4f, 0.4f, 0.4f);
        ma.setDiffuseColor(0.4f, 0.4f, 0.4f);
        ma.setSpecularColor(0.4f, 0.4f, 0.4f);
        ma.setEmissiveColor(0.4f, 0.4f, 0.4f);
        ma.setShininess(0.1f);
        
        ap.setMaterial(ma);
        
        
        
        SharedGroup sharedG = new SharedGroup();
        Box box = new Box(4,4,4,ap);
        //box.setAppearance(ap);
        //ColorCube box = new ColorCube();
        sharedG.addChild(box);
                
        sharedG.compile();
        
        int num = 56;
        TransformGroup[] tgs = new TransformGroup[num];
        
        for(int i=0; i<num; i++) {
            tempT3d.setIdentity();
            TransformGroup tg = new TransformGroup(tempT3d);
            tg.addChild(new Link(sharedG));
            root.addChild(tg);
            tgs[i] = tg;
        }
        
            
        BoxBehavior boxInt = new BoxBehavior(tgs);
        boxInt.setSchedulingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000.0));
        root.addChild(boxInt);
        

        
        Material roomMa = new Material();
        roomMa.setAmbientColor(0.4f, 0.45f, 0.4f);
        roomMa.setDiffuseColor(0.4f, 0.45f, 0.4f);
        roomMa.setSpecularColor(0.4f, 0.45f, 0.4f);
        roomMa.setEmissiveColor(0.4f, 0.45f, 0.4f);
        roomMa.setShininess(0.0f);
        
        PolygonAttributes roomPa = new PolygonAttributes();
        roomPa.setCullFace(roomPa.CULL_FRONT);
        roomPa.setBackFaceNormalFlip(true);        
        
        Appearance roomAp = new Appearance();
        roomAp.setMaterial(roomMa);
        roomAp.setPolygonAttributes(roomPa);
        
        tempT3d.setIdentity();
        TransformGroup roomTG = new TransformGroup(tempT3d);
        Box room = new Box(160f, 80f, 160f, Box.GENERATE_NORMALS, roomAp);
        roomTG.addChild(room);
        root.addChild(roomTG);
        
        return root;
    }
    
    
    
    private TransformGroup createView(Canvas3D canvas) {
        //create the ViewPlatform
        ViewPlatform vp = new ViewPlatform();
        //vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW);
        vp.setActivationRadius(View.NOMINAL_SCREEN);
        
        //create a TransformGroup to scale the ViewPlatform
        //(and hence View)
        TransformGroup viewTG = new TransformGroup();
        viewTG.setCapability(TransformGroup.ALLOW_LOCAL_TO_VWORLD_READ);
        viewTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        viewTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        viewTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ); 
        viewTG.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
        viewTG.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);        

        
        //attach the ViewPlatform to the TransformGroup
        viewTG.addChild(vp);
        
        // create ViewPlatform BranchGroup
        BranchGroup vpBG = new BranchGroup();
        
        //attach the TransformGroup to the BranchGroup
        vpBG.addChild(viewTG);        
        
        
        //create the PhysicalBody and PhysicalEnvironment for the View
        //and attach to the View
        PhysicalBody pb = new PhysicalBody();
        PhysicalEnvironment pe = new PhysicalEnvironment();
        
        //create the View object
        View view = new View();
        view.setPhysicalEnvironment(pe);
        view.setPhysicalBody(pb);
        
        
        //attach the View to the ViewPlatform
        view.attachViewPlatform(vp);
        
        //set the near and far clipping planes for the View
        view.setFrontClipDistance( 1f );
        view.setBackClipDistance( 1000f );

        locale.addBranchGraph(vpBG);
        
        view.addCanvas3D(canvas);

        return viewTG;
        
    }

    
    private ImageRenderCanvas3D[] createCanvasArray(ImageBlitCanvas3D c) {
        GraphicsConfiguration config =  c.getGraphicsConfiguration();
        Screen3D sOn = c.getScreen3D();
        ImageRenderCanvas3D[] cs = new ImageRenderCanvas3D[3];
        
        for(int i=0; i<3; i++) {
            // Raster for OffScreen endereing
            BufferedImage bImage = new BufferedImage(200, 110 , BufferedImage.TYPE_INT_ARGB);
            
            c.addImage(i*(250)+50, 450, bImage);
            
            cs[i] = new ImageRenderCanvas3D(config, bImage);

            // set the offscreen to match the onscreen
            Screen3D sOff = cs[i].getScreen3D();
            sOff.setSize(sOn.getSize());
            sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth());
            sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight());
        }
        
        return cs;
    }    
    
        
    // Window Listner
    public void windowActivated(final WindowEvent e) {}
    public void windowDeactivated(final WindowEvent e) {}
    public void windowIconified(final WindowEvent e) {}
    public void windowDeiconified(final WindowEvent e) {}
    public void windowOpened(final WindowEvent e) {}
    public void windowClosing(final WindowEvent e) {
        virtualU.removeAllLocales();
        theFrame.dispose();
    }
    public void windowClosed(final WindowEvent e) {
        System.exit(0);
    }
    
    
    class BoxBehavior extends Behavior {
        private Alpha alpha;
        private Alpha taAlpha;
        private TransformGroup[] tgs;
        
        
        private Vector3f[] trans;
        private Vector3f[] offs;
        
        // helper
        private Transform3D actualT3d   = new Transform3D();
        private Vector3f    temp        = new Vector3f();
        
        private int current = 0;
        
        WakeupCondition criteria = new WakeupOnElapsedFrames(0);
        
        TransparencyAttributes[] tas;
        
        BoxBehavior(TransformGroup[] tgs) {
            this.tgs = tgs;            
            
            int numTGs = tgs.length;
            trans = new Vector3f[numTGs];
            offs  = new Vector3f[numTGs];
                        
            for(int i=0; i<numTGs; i++) {
                tgs[i].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
                tgs[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                tgs[i].setCapabilityIsFrequent(TransformGroup.ALLOW_TRANSFORM_WRITE);
                offs[i]  = new Vector3f();
            }
        }
        
        private void saveTranlations() {
            int numTGs = tgs.length;
            for(int i=0; i<numTGs; i++) {
                trans[i] = new Vector3f();
                tgs[i].getTransform(actualT3d);
                actualT3d.get(trans[i]);
            }
        }
                
        private void setOne() {
            int curt = 6;
            int p = 0;
            for(int i=0; i< curt; i++) {
                for(int j=0; j<curt; j++) {
                    for(int l=0; l<curt; l++) {
                        if(isEdge(i,j,l, curt)) {
                            offs[p].set((-curt/2f+i+.5f)*20, (-curt/2f+l+.5f)*20, (-curt/2f+j+.5f)*20);
                            p++;
                        }
                    }
                }
            }
        }
        
        private void setTwo() {
            int curt = 6;
            int p = 0;
            for(int i=0; i< curt; i++) {
                for(int j=0; j<curt; j++) {
                    for(int l=0; l<curt; l++) {
                        if(isEdge(i,j,l, curt)) {
                            offs[p].set(-(-curt/2f+i+.5f)*20, -(-curt/2f+l+.5f)*20, -(-curt/2f+j+.5f)*20);
                            p++;
                        }
                    }
                }
            }
        }
        
        private boolean isEdge(int i, int j, int l, int num) {
            int cnt =0;
            num--;
            if(i == 0 || i == num)
                cnt++;
            if(j == 0 || j == num)
                cnt++;
            if(l == 0 || l == num)
                cnt++;
            if(cnt > 1)
                return true;
            else
                return false;
        }
        
        public void initialize() {
            wakeupOn(criteria);
        }
        
                
        public void processStimulus(java.util.Enumeration enumeration) {
            switch(current) {
                case 0:
                    saveTranlations();
                    setOne();
                    wakeupOn(criteria);
                    current++;
                    return;
                case 1:
                    alpha = new Alpha();
                    alpha.setMode(Alpha.INCREASING_ENABLE);// | Alpha.DECREASING_ENABLE);
                    alpha.setIncreasingAlphaDuration(5000);
                    //alpha.setDecreasingAlphaDuration(5000);
                    //alpha.setIncreasingAlphaRampDuration(500);
                    alpha.setLoopCount(1);
                    alpha.setStartTime(System.currentTimeMillis() + 5000);
                    
                    taAlpha = new Alpha();
                    taAlpha.setMode(Alpha.INCREASING_ENABLE);// | Alpha.DECREASING_ENABLE);
                    taAlpha.setIncreasingAlphaDuration(5000);
                    //alpha.setDecreasingAlphaDuration(5000);
                    //alpha.setIncreasingAlphaRampDuration(500);
                    taAlpha.setLoopCount(1);
                    taAlpha.setStartTime(System.currentTimeMillis() + 10000);
                    
                    current++;
                    break;
                case 2:
                    if(alpha.finished())
                        current++;
                    break;
                case 3:
                    saveTranlations();
                    setTwo();
                    wakeupOn(criteria);
                    current++;
                    return;
                case 4:
                    //alpha = new Alpha();
                    alpha.setMode(Alpha.INCREASING_ENABLE);// | Alpha.DECREASING_ENABLE);
                    alpha.setIncreasingAlphaDuration(5000);
                    alpha.setStartTime(System.currentTimeMillis() + 10000);
                    
                    current++;
                    break;
                case 5:
                    if(alpha.finished())
                        current++;
                    break;
                    
                default:
                    return;
            }
            
            float factor = (float) Math.pow(alpha.value(), 0.3);                        
            
            
            for(int i=0; i<tgs.length; i++) {
                temp.set(offs[i]);
                temp.scaleAdd(factor, trans[i]);
                tgs[i].getTransform(actualT3d);
                actualT3d.setTranslation(temp);
                tgs[i].setTransform(actualT3d);
            }
            
            wakeupOn(criteria);
        }
        
    }    

        public static void main(String[] argv) {
                Demo demo = new Demo();
        }

}
