/// 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;
  }
 /**
  * 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;
 }
  /**
   * 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;
  }