/**
   * If only one toolhead is used, a cool command for the unused head is added to this gcode source
   * object. Created to avoid smell/problems for single prints on a dual machine when prior build
   * was cancelled, or a toolhead is left hot from pre-heating.
   */
  public void coolUnusedToolhead() {
    GCodeCommand gcode;
    String line;

    double tval;
    int additionPoint = 0;
    boolean addPointFound = false;

    boolean seenT0 = false;
    boolean seenT1 = false;

    for (Iterator<String> it = source.iterator(); it.hasNext(); ) {
      line = it.next();
      gcode = new GCodeCommand(line);

      tval = gcode.getCodeValue('T');

      if (tval == 0) seenT0 = true;
      if (tval == 1) seenT1 = true;

      if (!addPointFound) additionPoint++;

      if (gcode.getCodeValue('M') == 104) addPointFound = true;

      if (seenT0 && seenT1) return;
    }

    if (seenT0 && !seenT1) add(additionPoint, "M104 T1 S0");
    if (seenT1 && !seenT0) add(additionPoint, "M104 T0 S0");
  }
  /// Runs through this gcode file, swapping all references to the the current toolhead
  /// to instread reference the specified toolhead.  Alters select G, M and T Codes.
  public void changeToolhead(ToolheadAlias tool) {
    GCodeCommand gcode;
    int value;
    String line;
    /// FUTURE: create a synchronize block here someday
    ArrayList<String> newSource = new ArrayList<String>(source.size());
    for (Iterator<String> it = source.iterator(); it.hasNext(); ) {
      line = it.next();
      gcode = new GCodeCommand(line);

      if (gcode.hasCode('T')) {
        value = (int) gcode.getCodeValue('T');
        if (value != tool.number) {
          if (value == 0) line = line.replace("T0", "T1");
          else if (value == 1) line = line.replace("T1", "T0");
        }
      }
      if (gcode.getCodeValue('G') == 54 && !(tool.getRecallOffsetGcodeCommand().equals("G54"))) {
        line = line.replace("G54", tool.getRecallOffsetGcodeCommand());
      }
      if (gcode.getCodeValue('G') == 55 && !(tool.getRecallOffsetGcodeCommand().equals("G55"))) {
        line = line.replace("G55", tool.getRecallOffsetGcodeCommand());
      }
      newSource.add(line);
    }

    source = newSource;
  }
 /**
  * This gets the first feedrate used in a layer
  *
  * @param l
  * @return
  */
 private String getFirstFeedrate(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = 0; i < search.size(); i++) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('F') != -1)
       return "F" + Base.getGcodeFormat().format(gcode.getCodeValue('F'));
   }
   return "";
 }
 /**
  * Apparently skeinforge does not have all the moves in a layer at the same height. The first one
  * is frequently lower than the following ones, so this function finds the last height listed in a
  * layer
  *
  * @param l the layer in which to look
  * @return the layer height (maybe)
  */
 private Double getLayerZ(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = search.size() - 1; i >= 0; i--) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('G') == 1 && gcode.hasCode('Z')) {
       return gcode.getCodeValue('Z');
     }
   }
   return null;
 }
 /**
  * gets the first G1 from a layer, returns the position of X, Y, Z axes
  *
  * @param l
  * @return
  */
 private Point5d getFirstPosition(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = 0; i < search.size(); i++) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('G') == 1) {
       Point5d result = new Point5d();
       result.setX(gcode.getCodeValue('X'));
       result.setY(gcode.getCodeValue('Y'));
       result.setZ(gcode.getCodeValue('Z'));
       return result;
     }
   }
   return null;
 }
  /**
   * Takes a GCodeSource, assumed to be lacking any start- or end- specific blocks of code and to be
   * in order of increasing layer height, and returns a LinkedList of Layers. Each Layer should
   * contain codes for a single height. The list should be in sorted order from lowest height to
   * highest. We use a LinkedList because all this is internal, so it doesn't change any interfaces
   * if we want to change it, and a LinkedList doubles as a Queue, which is handy for doMerge().
   *
   * <p>WARNING: This code assumes that the source gcode follows one of two formats: Either the
   * extruder is turned off at the end of each layer using an M103, Or the gcode uses 5D, and there
   * are no M103/M101/M108 commands
   *
   * <p>These should be safe assumptions for any code generated by Skeinforge
   *
   * @param source
   * @return
   */
  private LinkedList<Layer> testParseLayers(final GCodeSource source) {
    /*
     * So this is a little more complicated than just breaking up stuff by Z height,
     * there may be M commands between layers, some of which belong to the previous
     * layer, and some to the next.
     * To get around this we:
     * Walk through the source
     *   // this assumes that every layer ends with the extruder off
     *   keep a trailing pointer to the last M103 we saw, and a count of the last layer height
     *   we saw when we see a new layer height, break off a new layer after the previous M103
     *
     *   but, with 5d, there won't be any M103, layers should have no associated pre/post Mcodes
     *
     */

    final LinkedList<Layer> layers = new LinkedList<Layer>();
    final Queue<String> read = new LinkedList<String>();

    // debug code///////////////////////////
    layers.add(
        new Layer(
            0d,
            new ArrayList<String>() {
              {
                add("(*************start layer*************)");
              }
            }));
    //////////////////////////////////////
    String lastM103 = null;
    double lastZHeight = Double.MIN_VALUE;
    for (String line : source) {
      GCodeCommand gcode = new GCodeCommand(line);

      if (gcode.getCodeValue('M') == 103) lastM103 = line;

      if (gcode.hasCode('Z')) {
        double newZ = gcode.getCodeValue('Z');

        // keeps us from creating an initial, empty layer
        if (lastZHeight == Double.MIN_VALUE) {
          lastZHeight = newZ;
        } else if (newZ > lastZHeight) {
          ArrayList<String> tmpLayer = new ArrayList<String>();

          // fill the tmpLayer with the accumulated lines, up to the
          // most recent "stop extruding" or until the queue is empty (5D)
          while (read.peek() != null && read.peek() != lastM103) tmpLayer.add(read.poll());

          // Also grab the M103, if present
          if (read.peek() == lastM103) tmpLayer.add(read.poll());

          // put it in a new layer
          layers.add(new Layer(lastZHeight, tmpLayer));

          // record our next layer height
          lastZHeight = newZ;
        }
      }

      read.add(line);
    }

    // debug code///////////////////////////
    layers.add(
        new Layer(
            0d,
            new ArrayList<String>() {
              {
                add("(*************end layer*************)");
              }
            }));
    //////////////////////////////////////
    return layers;
  }