import java.net.*;
import java.awt.*;
import java.util.Enumeration;
import java.applet.Applet;
import java.awt.event.*;
import javax.vecmath.*;
import javax.media.j3d.*;

import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;


import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import com.sun.j3d.utils.applet.MainFrame;

import com.sun.j3d.utils.geometry.*;

public class TestBed4 extends Applet
{
  private SimpleUniverse universe = null;

  Appearance boxApp;

  // Place transform groups near the boxes to act as slots
  // to receive the cones.
  private TransformGroup[] slot = new TransformGroup[3];
  private boolean slotFull[] = new boolean[3];

  // Use BranchGroup to attach/detach cones to live scene.
  private BranchGroup[] cones = new BranchGroup[3];
  private int coneLocation[] = new int[2];
  private Appearance[] coneApps = new Appearance[2];
  private Color3f[] coneColors = new Color3f[]
  {
    new Color3f(0.8f, 0.1f, 0.1f),
    new Color3f(0.1f, 0.8f, 0.1f),
  };

  // Remember which cone is "in hand"
  private int coneInHand=-1;

  static public void main(String[] args)
  {
    new MainFrame(new TestBed4(), 300, 300);
  }

  public TestBed4()
  {
    // Make the appearance for the boxes.
    {
      boxApp = new Appearance();
      Material mat = new Material();
      mat.setLightingEnable(true);
      mat.setEmissiveColor(0.1f, 0.1f, 0.1f);
      mat.setDiffuseColor(0.3f, 0.3f, 0.7f);
      boxApp.setMaterial(mat);
     }

    // Make the appearances of the cones.
    int i;
    for(i=0; i<coneApps.length; i++)
    {
      coneApps[i] = new Appearance();
      Material mat = new Material();
      mat.setLightingEnable(true);
      mat.setEmissiveColor(0.1f, 0.1f, 0.3f);
      mat.setDiffuseColor(coneColors[i]);
      coneApps[i].setMaterial(mat);
    }

  }

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

    // Create the canvas
    GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
    GraphicsConfiguration gcfg =
              GraphicsEnvironment.getLocalGraphicsEnvironment().
              getDefaultScreenDevice().getBestConfiguration(template);
    Canvas3D canvas = new Canvas3D(gcfg);
    canvas.setSize(300, 300);
    add("Center", canvas);

    // Create the universe.
    universe = new SimpleUniverse(canvas);

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

    // Make the 3 boxes that will act as cone holders.
    makeBox(mainBranch, 0.0f, 0);
    makeBox(mainBranch, -3.2f, 1);
    makeBox(mainBranch, 3.2f, 2);

    // Make the two cones.
    makeCone(0);
    makeCone(1);

    // Add light to the scene.
    PointLight lt = new PointLight();
    lt.setEnable(true);
    lt.setInfluencingBounds(
      new BoundingSphere(new Point3d(0.0,0.0,0.0), Float.MAX_VALUE));
    mainBranch.addChild(lt);

    // Add a mouse selection behavior.
    SelectBehavior selbeh = new SelectBehavior(canvas, mainBranch, this);
    selbeh.setSchedulingBounds(new BoundingSphere(new Point3d(), 5000.0));
    mainBranch.addChild(selbeh);

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

  // Make a box with the given ID.
  private void makeBox(Group br, float location, int ID)
  {
    // Create the transform group to place the box.  Enable pick
    // selection of the transform group.
    Transform3D xform = new Transform3D();
    xform.setTranslation(new Vector3f(location, 0.0f, -12.0f));
    TransformGroup grp = new TransformGroup(xform);
    grp.setCapability(grp.ENABLE_PICK_REPORTING);

    // Store the box ID in the user data.
    grp.setUserData( new Integer(ID) );

    // Create the box geometry.
    Box box = new Box(1.5f, 1.5f, 1.5f, boxApp);
    grp.addChild( box );

    // Make the transform group that will act as the slot to accept
    // another object
    Transform3D slotxform = new Transform3D();
    slotxform.setTranslation(new Vector3f(0.0f, 0.0f, 2.0f));
    slot[ID] = new TransformGroup(slotxform);
    slot[ID].setCapability(Group.ALLOW_CHILDREN_EXTEND);
    slot[ID].setCapability(Group.ALLOW_CHILDREN_WRITE);
    slotFull[ID] = false;
    grp.addChild(slot[ID]);

    // Add the box to the scene.
    br.addChild(grp);
  }

  // Function to add a cone under a BranchGroup to a slot.
  private void addToSlot(int whichslot, BranchGroup thing)
  {
    slot[whichslot].addChild(thing);
    slotFull[whichslot] = true;
  }

  // Make a cone with the given ID.  Place it in the slot
  // of the same ID.
  public void makeCone(int ID)
  {
    // Place the cone under a branchgroup to allow attach/detach from
    // a live scene.
    cones[ID] = new BranchGroup();
    cones[ID].setCapability(BranchGroup.ALLOW_DETACH);

    // Create the geometry.
    Cone cone = new Cone();
    cone.setAppearance(coneApps[ID]);

    // Place under a transform group for pick reporting.
    TransformGroup grp = new TransformGroup();
    grp.setCapability(grp.ENABLE_PICK_REPORTING);

    // Record the cone ID in the user data.
    // Add 10 to denote that it is a cone, not a box.
    grp.setUserData( new Integer(ID + 10) );

    // Add the cone to the scene.
    grp.addChild(cone);
    cones[ID].addChild(grp);
    addToSlot(ID, cones[ID]);

    // Remember which slot the cone is in.
    coneLocation[ID] = ID;
  }

  // Process a box or cone event.
  public void selectionEvent(Object obj)
  {
    if (obj instanceof Integer)
    {
      int id = ((Integer)obj).intValue();

      // If the id > 10, it is a cone.
      if (id>=10)
      {
        id -= 10;
        System.out.println("Selected cone " + id);
        if (coneInHand==-1)
        {
          cones[id].detach();
          slotFull[coneLocation[id]] = false;
          coneInHand = id;
        }
      } // END IF id is a cone

      else // Otherwise, it is a box
      {
        System.out.println("Selected box " + id);
        if (coneInHand!=-1 && !slotFull[id] )
        {
          addToSlot(id, cones[coneInHand]);
          coneLocation[coneInHand] = id;
          coneInHand=-1;
        }
      } // END ELSE box selected
    } // END IF user data is integer

  } // END FUNC selectionEvent()

}


class SelectBehavior extends Behavior
{
  private PickCanvas pickScene;
  private WakeupCriterion wakeup = null;
  private TestBed4 tester;

  public SelectBehavior(Canvas3D canvas, BranchGroup g, TestBed4 t)
  {
    pickScene = new PickCanvas(canvas, g);
    tester = t;
  }

  public void initialize()
  {
    wakeup = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
    wakeupOn (wakeup);
  }

  public void processStimulus(Enumeration criteria)
  {
    WakeupCriterion wakeup = null;
    AWTEvent[] evt = null;
    int xpos = 0, ypos = 0;

    while(criteria.hasMoreElements())
    {
      wakeup = (WakeupCriterion)criteria.nextElement();
      if (wakeup instanceof WakeupOnAWTEvent)
        evt = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
    }
    
    if (evt[0] instanceof MouseEvent)
    {
      MouseEvent mevent = (MouseEvent) evt[0];

      if (mevent.getID() == MouseEvent.MOUSE_PRESSED)
      {
        pickScene.setShapeLocation(mevent);
        PickResult result = pickScene.pickClosest();
        if (result != null)
        {

          TransformGroup xform =
            (TransformGroup)result.getNode(PickResult.TRANSFORM_GROUP);
          if (xform != null )
          {
            Object obj = xform.getUserData();
            tester.selectionEvent(obj);
          }
        }// END IF result!=null
      }// END IF MOUSE_PRESSED
      wakeupOn(wakeup);
    }// END IF MouseEvent
  }

}