import javax.media.j3d.BranchGroup;
import javax.media.j3d.Appearance;
import javax.vecmath.Point3d;


/**
 * This class provides a front-end interface for creating a BranchGroup 
 * by interpreting a 2-D FRACTINT-type L-system stored in a file.
 * (Currently, it's only been tested on the file "weed.lsys", but hey,
 * that's a start.)
 * For more information on L-systems, see:
 * <a href="http://spanky.triumf.ca/www/fractint/lsys/plants.html">
 * http://spanky.triumf.ca/www/fractint/lsys/plants.html</a>
 * <p>
 * Example usage:
 * <p>
 * <pre>
 *   Lsystem lsystem = new Lsystem();   // construct an L-system object
 *   lsystem.initLsys (initPoint, lineLength, initAngle);
 *                                      // init. starting Lsystem parameters
 *   lsystem.initDraw (thk, mult, cylEdges, debug);
 *                                      // initialize drawing settings
 *   lsystem.readLsystem(filename);     // read rules from an L-system file
 *   lsystem.setAppearance(branchApp, leafApp);   // set branch/leaf appearance
 *   BranchGroup lsysBg = lsystem.execute(depth);
 *                                      // interpret L-system to depth "depth,"
 *                                      // returning a BranchGroup
 * </pre>
 * <p> 
 * @author Scott Teresi, April 1999, www.teresi.us
 *
 */



public class Lsystem {

  Interpreter interpreter = new Interpreter();
  Rules rules = new Rules();
  State state = new State();


  /**
   * Constructs an L-system object with default parameters.
   */

  public Lsystem() {
  }


  /**
   * Interprets the L-system.
   * @param depth Number of recursive iterations of the L-system rules.
   */

  public BranchGroup execute (int depth) {
    interpreter.setState (state);
    return interpreter.execute(depth + 1, rules.getAxiom());
  }


  /**
   * Sets the Appearance of the branches and leaves of the L-system.
   * The leaves are simply the line segments added during the final iteration.
   * @param branchApp Appearance of the branches
   * @param leafApp Appearance of the leaves
   */

  public void setAppearance (Appearance branchApp, Appearance leafApp) {
    interpreter.setAppearance (branchApp, leafApp);
  }


  /**
   * Initialization parameters of the L-system.
   * @param initPoint starting point of L-system interpreter in 3-D space
   * @param lineLength initial line length
   * @param initAngle initial orientation in 2-D space
   */
   
  public void initLsys (Point3d initPoint, double lineLength,
			double initAngle) {
    state.curPos = initPoint;
    state.lineLength = lineLength;
    state.angle = initAngle;
  }


  /**
   * Initialization parameters affecting the L-system's geometry.
   * @param thk minimum radius of the cylinders (line segments)
   * @param mult multiplier for making longer line segments thicker
   *     (cylinder radius = thickness + mult * lineLength)
   * @param cylEdges number of edges (resolution) of the cylinders
   * @param debug flag to turn on debugging messages
   */

  public void initDraw (double thk, double mult, int cylEdges, boolean debug) {
    interpreter.initDraw (thk, mult, cylEdges, debug);
  }


  /**
   * Reads an L-system contained in a file.
   * @param filename the name of the file.
   */
   
  public boolean readLsystem(String filename) {

    LsystemsReader rdr;
    String line;
    int ch = 0;

    try {
      rdr = new LsystemsReader(filename);
    } catch (Exception e) {
      System.out.println("Error reading file " + filename + ".");
      return false;
    }

    while (ch != (int) '{') {
      ch = rdr.read();
    }

    line = rdr.readLine();
    if (!rdr.parseWord(line).equalsIgnoreCase("Angle")) {
      System.out.println("Missing " + (char) 34 + "Angle" + (char) 34 +
			 " keyword.");
      return false;
    }

    double angle;
    try {
      angle = (new Double(line.substring(6, line.length()))).doubleValue();
    } catch (Exception ex) {
      System.out.println("Missing angle value.");
      return false;
    }
    angle = 360.0 / angle;
    state.angleInc = angle;

    line = rdr.readLine();
    if (!rdr.parseWord(line).equalsIgnoreCase("Axiom")) {
      System.out.println("Missing " + (char) 34 + "Axiom" + (char) 34 +
			 " keyword.");
      return false;
    }

    line = line.substring(6, line.length());
    rules.setAxiom(line);

    while (!rdr.done) {
      line = rdr.readLine();
      if (!rdr.done)
	rules.add(line);
    }

    if (rules.size() < 1) {
      System.out.println("Could not find any rules in " + filename + ".");
      return false;
    }

    return true;

  }

}

