import java.util.Random;
import java.util.Vector;
import java.awt.Frame;
import java.awt.BorderLayout;
import java.awt.event.*;

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.utils.geometry.*;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.SceneBase;


/**
 * This Java application shows how to use the Lsystem class to put an
 * L-system in a Java 3D scene. Use the example L-system provided, "weed.lsys".
 * <p>
 * Compile with: "javac DrawLsystem.java"
 * <p>
 * Run with: "java -Xmx64m DrawLsystem weed.lsys" (if you have 64 megs RAM)
 * <p>
 * @author Scott Teresi, April 1999, www.teresi.us
 *
 */



public class DrawLsystem {


  int depth = 6;   // depth of Lsystem execution

  Color3f skyColor = new Color3f(0.0f, 0.2f, 1f);  // (0, .6, .2)
  Color3f groundColor = new Color3f(0.1f, .65f, 0.02f);
  int rotateSpeed = 60000;   // 20,000 ms per revolution


  public DrawLsystem(String filename) {

    Lsystem lsystem = new Lsystem();

    // set starting point, line length, init. angle
    lsystem.initLsys (new Point3d(0d, 0d, 0d), 8.0, 0.0);
    // set cylinder thickness, thickness multiplier, cylinder edges, debug flag
    lsystem.initDraw (.07, .02, 7, false);

    // set the color of the Appearance
    Material mat1 = new Material();
    mat1.setDiffuseColor(.9f, .2f, .05f);
    mat1.setAmbientColor(.18f, .05f, 0f);
    Appearance branchApp = new Appearance();
    branchApp.setMaterial(mat1);
    Appearance leafApp = new Appearance();
    Material mat2 = new Material();
    mat2.setDiffuseColor(0f, .7f, .05f);
    mat2.setAmbientColor(0f, .3f, 0f);
    leafApp.setMaterial(mat2);
    lsystem.setAppearance(branchApp, leafApp);

    // interpret the Lsystem
    lsystem.readLsystem(filename);
    BranchGroup lsysBg = lsystem.execute(depth);

    // build the scene
    BranchGroup mainBg = createUniverse();
    mainBg.addChild(drawGround());
    mainBg.addChild(lsysBg);

  }


  BranchGroup createUniverse() {

    // set up the display frame

    Frame frame = new Frame("Tree Display");
    frame.setLayout(new BorderLayout());
    WindowListener l = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
	System.exit(0);
      }
    };
    frame.addWindowListener(l);

    Canvas3D canvas = new Canvas3D(null);
    frame.setSize(800, 700);
    frame.add("Center", canvas);
    frame.setVisible(true);

    // create the universe

    VirtualUniverse u = new VirtualUniverse();
    Locale locale = new Locale(u);


    // set up the view

    // view transformations
    Transform3D transform = new Transform3D();
    transform.setTranslation(new Vector3d(0, 16, 75));
    Transform3D rotateX = new Transform3D();
    rotateX.rotX(-Math.PI/6);
    transform.mul(rotateX, transform);

    BranchGroup viewBranch = new BranchGroup();
    TransformGroup firstViewTrans = new TransformGroup(transform);
    TransformGroup viewTrans = new TransformGroup();
    ViewPlatform viewPlatform = new ViewPlatform();

    viewBranch.addChild(firstViewTrans);
    firstViewTrans.addChild(viewTrans);
    viewTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    viewTrans.addChild(viewPlatform);
    viewPlatform.setActivationRadius(1000.0f);

    View view = new View();
    view.attachViewPlatform(viewPlatform);
    view.addCanvas3D(canvas);
    view.setPhysicalBody(new PhysicalBody());
    view.setPhysicalEnvironment(new PhysicalEnvironment());
    view.setBackClipDistance(100000f);
    view.setViewPolicy(View.SCREEN_VIEW);
    view.setWindowResizePolicy(View.VIRTUAL_WORLD);   // doesn't work
    view.setWindowMovementPolicy(View.VIRTUAL_WORLD); // doesn't work

    viewBranch.compile();
    locale.addBranchGraph(viewBranch);


    // apply lighting, background color

    BranchGroup mainBg = new BranchGroup();
    mainBg.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
					       200.0);
    Background bgrnd = new Background(skyColor);
    bgrnd.setApplicationBounds(bounds);
    mainBg.addChild(bgrnd);

    PointLight light1 = new PointLight();
    light1.setPosition(new Point3f(70, 20, -50));
    light1.setColor(new Color3f(1.0f, 1.0f, 1.0f));
    light1.setInfluencingBounds(bounds);
    mainBg.addChild(light1);

    AmbientLight ambient1 = new AmbientLight(true, new Color3f(1f, 1f, 1f));
    ambient1.setInfluencingBounds(bounds);
    mainBg.addChild(ambient1);

    // set up rotation animation behavior

    BranchGroup objRoot = new BranchGroup();
    TransformGroup objSpin = new TransformGroup();

    objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objSpin.addChild(mainBg);
    objRoot.addChild(objSpin);
    RotationInterpolator rotator =
                 new RotationInterpolator(new Alpha(-1, rotateSpeed), objSpin);
    BoundingSphere boundSphere = new BoundingSphere();
    rotator.setSchedulingBounds(boundSphere);
    objSpin.addChild(rotator);

    objRoot.compile();
    locale.addBranchGraph(objRoot);

    return mainBg;

  }



  BranchGroup drawGround() {

    BranchGroup bg = new BranchGroup();
    bg.setCapability(BranchGroup.ALLOW_BOUNDS_READ);

    // create ground

    int numPoints = 4;

    Point3f[] points;
    points = new Point3f[numPoints];
    points[0] = new Point3f(-20f, 0f, 20f);
    points[1] = new Point3f(20f, 0f, 20f);
    points[2] = new Point3f(20f, 0f, -20f);
    points[3] = new Point3f(-20f, 0f, -20f);

    Color3f[] colors = new Color3f[4];
    for (int i = 0; i < 4; i ++)
      colors[i] = groundColor;

    int[] indices = new int[4];
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 3;

    int[] stripCounts = new int[1];
    stripCounts[0] = 4;

    GeometryInfo geoInfo = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
    geoInfo.setStripCounts(stripCounts);
    geoInfo.setCoordinates(points);
    geoInfo.setCoordinateIndices(indices);
    geoInfo.setColors(colors);
    geoInfo.setColorIndices(indices);

    Triangulator tr = new Triangulator();
    tr.triangulate(geoInfo);
    NormalGenerator nm = new NormalGenerator();
    nm.generateNormals(geoInfo);

    Shape3D shape = new Shape3D(geoInfo.getGeometryArray(), new Appearance());

    bg.addChild(shape);
    bg.compile();

    return bg;
  }



  public static void main (String args[]) {

    String file = new String();

    if (args.length == 1)
      file = args[0];
    else {
      System.out.println("Please specify a file containing an L-System.");
      System.exit(0);
    }

    // create an instance of this class
    DrawLsystem drawLsystem = new DrawLsystem(file);

  }


}
