import jdsl.core.algo.traversals.*;
import jdsl.core.api.*;
import java.awt.*;
import java.awt.geom.*;

/** 
 * The first step in drawing the tree. The tree is drawn so:
 *   - Edges are straight lines
 *   - nodes are centered above the drawings of their children
 *   - bounding boxes of the drawings of subtrees rooted at siblings are drawn
 *     adjacent.
 *   - The width of a bounding box is the maximum of the label width and the
 *     sum of the bounding boxes of the children.
 *
 * @author Lucy Perry (lep)
 * @version JDSL 2
*/
public class BoundingBoxCalculator extends EulerTour {
  
  /* ************************************ */ 
  /* The members described in the lesson. */
  /* ************************************ */ 
  //b6.4
  protected int offset = 50;        // size of the grid squares
  protected int width  = 0;         // a running total width of the explored tree drawing

  protected int depth = 0;          // a running total depth of the explored tree drawing
  protected Graphics g;             // the graphics object where the tree will be drawn
  protected FontMetrics fm;         // the fontMetrics object for g
  //e6.4
  
  /**
   * When a node is visited first in the Euler Tour we know the upper-left hand 
   * corner of its bounding box, which we store as decorations.
   */
  //b6.5
  protected void visitFirstTime(Position pos){
    pos.set("y", new Integer(depth));
    pos.set("x", new Integer(width));
    depth += offset;
  }
  //e6.5
  
  /**
   * When a node is visited last in the Euler Tour we know the width 
   * of its bounding box, which we store as a decoration.
   */
  //b6.6
  protected void visitLastTime(Position pos){
    int textWidth = textWidth(pos);
    int shift = 0;
    int x = ((Integer)pos.get("x")).intValue();
    int boxWidth = width-x;
    if (textWidth>boxWidth) {
      int delta = textWidth-boxWidth;
      boxWidth = textWidth;
      width += delta;
      shift = delta/2;
    }
    pos.set("width", new Integer(boxWidth));
    // The distance that the children's bounding boxes need to
    // be shifted in the drawing.
    pos.set("shift", new Integer(shift));
    depth -= offset;
  }
  //e6.6
  
  /* 
   * Sets all attributes for an external node
   */
  //b6.7 
  protected void visitExternal( Position pos ) { 
    pos.set("y", new Integer(depth));
    pos.set("x", new Integer(width));
    int textWidth = textWidth(pos);
    width+=textWidth;
    pos.set("width", new Integer(textWidth));
    pos.set("shift", new Integer(0));
  } 
  //e6.7

  /* ************************************ */ 
  /* Members not described in the lesson. */
  /* ************************************ */ 
      
  protected int pad=5;              // the distance to separate bounding boxes;
  
  public BoundingBoxCalculator(Graphics gg) {
    g=gg;
    fm = g.getFontMetrics();
  }
  
  /**
   * Calculates the width of the drawing of the node label.  Stores 
   * the attributes needed to calculate the exact position to draw the
   * label.
   */
  protected int textWidth(Position pos) {
    String str=pos.element().toString();
    Rectangle2D bounds = fm.getStringBounds(str,g);
    pos.set("bounds", bounds);
    pos.set("ascent", new Integer(fm.getMaxAscent()));
    pos.set("descent", new Integer(fm.getMaxDescent()));
    return (int)bounds.getWidth()+pad;
  }
}