//package com.saic.celebration.TestJ3D;

import java.net.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import javax.vecmath.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.audioengines.javasound.JavaSoundMixer;
import com.sun.j3d.utils.applet.MainFrame;

public class TestJ3D extends Applet
  implements ActionListener
{
  private TextArea textArea;
  private My3DWindow win;

  public TestJ3D()
  {
  }

  public void init()
  {
    setLayout(new BorderLayout());

    textArea = new TextArea( "Inspecting your Java installation...\n", 20, 10 );

    textArea.append( "Java Version: " + getProp("java.version") + "\n" );
    textArea.append( "Vendor: " + getProp("java.vendor") + "\n" );

    textArea.append("Looking for Java3D version 1.2.1 or later.\n");
    textArea.append("Installed Java3D version: ");

    boolean j3d_installed = true;
    try
    {
      Class c = Class.forName("javax.media.j3d.BranchGroup");
      textArea.append( c.getPackage().getImplementationVersion() + "\n" );
      textArea.append( "Java 3D upgrade required? " +
        c.getPackage().isCompatibleWith("1.2.1") + "\n");
    }
    catch( Exception e )
    {
      textArea.append( "Java3D not installed!\n" );
      j3d_installed = false;
    }

    try
    {
      if(j3d_installed) do3DStuff();
    }
    catch( Exception e )
    {
      textArea.append( "Caught an exception while trying to run 3D:\n" );
      textArea.append( e.getMessage() );
    }

    add("Center", textArea);
  }

  private void do3DStuff()
    throws Exception
  {
    win = new My3DWindow( this );
  }

  public void start()
  {
    if( win != null )
      win.canvas.startRenderer();
  }

  public void stop()
  {
//    canvas.stopRenderer();
  }

  public void destroy()
  {
    stop();
  }

  public static void main(String args[])
  {
     System.out.println("In main!");
    new MainFrame(new TestJ3D(), 380, 380);
 
  }
 
  public void actionPerformed(ActionEvent e)
  {
     String command = e.getActionCommand();
     if (command.equals("Toggle Text"))
     {
       win.toggleText();
     }
  }
 
  private String getProp(String key)
  {
    String value;
    try
    {
      value = System.getProperty(key);
 
    }catch(SecurityException e)
    {
      value = e.toString();
    }
 
    return( value );
  }

}

class My3DWindow
{
  private SimpleUniverse universe;
  protected Canvas3D canvas;
  private Label label;
  private BranchGroup mainBranch, textBranch;


  protected My3DWindow( Applet c )
    throws Exception
  {
    label = new Label("xx.xx fps");
    c.add("South", label);

    GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
    GraphicsConfiguration gcfg =
      GraphicsEnvironment.getLocalGraphicsEnvironment().
        getDefaultScreenDevice().getBestConfiguration(template);
    canvas = new MyCanvas3D(gcfg, label);
    canvas.stopRenderer();
    canvas.setSize( new Dimension(300,300) );
    c.add("West", canvas);

    Button button = new Button("Toggle Text");
    c.add("North", button);
    button.addActionListener( (ActionListener)c );

    // The virtual universe.
    universe = new SimpleUniverse(canvas);

    // Add sound to environment.
    PhysicalEnvironment env = (universe.getViewer()).getPhysicalEnvironment();
    JavaSoundMixer javaSoundMixer = new JavaSoundMixer(env);
    javaSoundMixer.initialize();
    env.setAudioDevice(javaSoundMixer);

    // Create the main branch.
    mainBranch = new BranchGroup();
    mainBranch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
    mainBranch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

    // Create the area to add geometry.
    Transform3D xform = new Transform3D();
    xform.setTranslation( new Vector3d(0, 0, -30) );
    TransformGroup area = new TransformGroup(xform);
    mainBranch.addChild(area);

    // Add a rotating cone.
    makeCone(area);

    // Make the light that will rotate about +Y axis every 3.25 secs.
    Transform3D axis1 = new Transform3D();
    makeLight(area, axis1, new Color3f(0.2f, 0.8f, 0.7f), 3250, true);

    // Make the light that will rotate about the -Z axis every 8.5 secs.
    Transform3D axis2 = new Transform3D();
    axis2.rotX(-Math.PI*0.5);
    makeLight(area, axis2, new Color3f(0.8f, 0.8f, 0.2f), 8500, false);


    // Add some text.
    textBranch = makeText();
    mainBranch.addChild(textBranch);

    // Make the main branch live.
    universe.addBranchGraph(mainBranch);
  }

  protected void toggleText()
  {
    if (textBranch.isLive())
    {
      textBranch.detach();
    }
    else
    {
      mainBranch.addChild(textBranch);
    }
  }

  // Make a rotating cone.
  private void makeCone(Group parent)
  {
    // Create a transform group that will be rotated.
    TransformGroup coneGrp = new TransformGroup();
    parent.addChild(coneGrp);
    coneGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    // Create the box appearance.
    Appearance app = new Appearance();
    Material mat = new Material();
    mat.setDiffuseColor(0.8f, 0.6f, 0.7f);
    mat.setEmissiveColor(0.1f, 0.1f, 0.1f);
    mat.setLightingEnable(true);
    app.setMaterial(mat);

    // Create the box.
    Cone cone = new Cone(5.0f, 10.0f, Primitive.GENERATE_NORMALS,
                         60, 20, app);
    coneGrp.addChild(cone);

    // Create the rotation behavior.
    Transform3D rotAxis = new Transform3D();
    rotAxis.rotZ(Math.PI*0.5);
    RotationInterpolator rotBeh =
      new RotationInterpolator(new Alpha(-1, 7700), coneGrp);
    rotBeh.setAxisOfRotation(rotAxis);
    rotBeh.setSchedulingBounds(
      new BoundingSphere(new Point3d(), Float.MAX_VALUE) );
    parent.addChild(rotBeh);
  }


  // Make a rotating box.
  private void makeLight(Group parent, Transform3D rotAxis, Color3f color,
                         long duration, boolean addsound)
  {
    // Make the transform group to rotate.
    TransformGroup rotGrp = new TransformGroup();
    parent.addChild(rotGrp);
    rotGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    // Make the group to hold the light and the sphere.
    Transform3D pos = new Transform3D();
    pos.setTranslation(new Vector3f(12.0f, 0.0f, 0.0f));
    TransformGroup lightGrp = new TransformGroup(pos);
    rotGrp.addChild(lightGrp);

    // Make the light.
    PointLight lt = new PointLight();
    lt.setColor(color);
    lt.setInfluencingBounds(
      new BoundingSphere(new Point3d(), Float.MAX_VALUE) );
    lightGrp.addChild(lt);

    // Make the sphere.
    Appearance app = new Appearance();
    Material mat = new Material();
    mat.setEmissiveColor(color);
    mat.setDiffuseColor(color);
    app.setMaterial(mat);
    lightGrp.addChild(new Sphere(1.0f, app));

    // Add sound if requested
    if (addsound)
    {
      lightGrp.addChild(makeSound());
    }

    // Create the rotation behavior.
    Alpha alpha = new Alpha();
    alpha.setMode(alpha.DECREASING_ENABLE);
    alpha.setDecreasingAlphaDuration(duration);
    RotationInterpolator rotBeh = new RotationInterpolator(alpha, rotGrp);
    rotBeh.setAxisOfRotation(rotAxis);
    rotBeh.setSchedulingBounds(
      new BoundingSphere(new Point3d(), Float.MAX_VALUE) );
    parent.addChild(rotBeh);

  }

  private BranchGroup makeText()
  {
    BranchGroup branch = new BranchGroup();
    branch.setCapability(BranchGroup.ALLOW_DETACH);
    Text2D modulateText = new Text2D("Java3D", 
                                     new Color3f(1f, 0f, 0f), 
                                     "Helvetica", 24, Font.BOLD);
    TextureAttributes textureAttrib = modulateText.getAppearance().getTextureAttributes();
    if (textureAttrib == null) {
           textureAttrib = new TextureAttributes();
           modulateText.getAppearance().setTextureAttributes(textureAttrib);
    }
    textureAttrib.setTextureMode(TextureAttributes.MODULATE);
    modulateText.getAppearance().getTransparencyAttributes().setTransparency(0.5f);
    Transform3D textXform = new Transform3D();
    textXform.setTranslation( new Vector3d(-5, 0, -15) );
    textXform.setScale(30f);
    TransformGroup textXformGroup = new TransformGroup(textXform);
    textXformGroup.addChild(modulateText);
    branch.addChild(textXformGroup);

    return(branch);
  }

  private Sound makeSound()
  {
    BoundingSphere bounds =
      new BoundingSphere(new Point3d(0,0,0), Double.MAX_VALUE);
    float[] distance = new float[]
    {
      0f,
      50f,
    };
    float[] gain = new float[]
    {
      1f,
      0f,
    };

    PointSound sound = null;
    URL soundURL = getClass().getResource("boing.wav");
    System.out.println("soundURL = " + soundURL );
    MediaContainer soundMedia = new MediaContainer(soundURL);
    sound = new PointSound(soundMedia, 3.0f, 0f, 0f, 0f);
    sound.setDistanceGain(distance, gain);
    sound.setSchedulingBounds(bounds);
    sound.setLoop(1000);
    sound.setEnable(true);

    return(sound);
  }

}

class MyCanvas3D extends Canvas3D
{
  private long lastTime=-1;
  private long frameCount=0;
  private Label myLabel;
  private StringBuffer buffer = new StringBuffer(15);
  private DecimalFormat format;

  MyCanvas3D(GraphicsConfiguration gcfg, Label label)
  {
    super(gcfg);
    myLabel = label;
    format = new DecimalFormat("###.00");
  }

  public void postRender()
  {
        super.postRender();
    frameCount++;

    long time=System.currentTimeMillis();
    if (lastTime==-1)
    {
      lastTime=time;
      return;
    }

    else
    {
      long elapsed = time-lastTime;
      if (elapsed >= 2000)
      {
        double fps = ((double)(frameCount) / (double)(elapsed)) * 1000.0;
        myLabel.setText(format.format(fps) + " fps");
        frameCount=0;
        lastTime = time;
      }
    }

  }  // END postRender()

}
