/**
   * This method handles shuffling together two gcodes, it first executes preprocessing and then
   * hands the gcodes off to Layer_Helper
   */
  public void combine() {
    MutableGCodeSource left, right;

    // load our layers into something we can use
    left = new MutableGCodeSource(leftFile);
    right = new MutableGCodeSource(rightFile);

    // remove  some tags we don't want/get
    SkeinforgePostProcessor.stripNonLayerTagComments(left);
    SkeinforgePostProcessor.stripNonLayerTagComments(right);

    // load our layers into something we can *really* use
    LinkedList<Layer> leftLayers = parseLayers(left);
    LinkedList<Layer> rightLayers = parseLayers(right);

    // merge our layers into one list
    final LinkedList<Layer> layers = doMerge(leftLayers, rightLayers);

    // refresh and repopulate our result
    result = new MutableGCodeSource();
    for (Layer l : layers) {
      result.add(l.getCommands());
    }

    // add start gcode, updated based on settings
    SkeinforgePostProcessor.prependAndModifyStartCode(result, startGCode);
    // add end code
    Layer endLayer = new Layer(Double.MAX_VALUE, endGCode.asList());
    result.add(endLayer.toString());
    // interlace progress updates
    result.addProgressUpdates();
  }
  public DualStrusionConstruction(
      File leftFile,
      File rightFile,
      MutableGCodeSource startSource,
      MutableGCodeSource endSource,
      MachineType type,
      boolean useWipes) {
    this.leftFile = leftFile;
    this.rightFile = rightFile;
    this.useWipes = useWipes;
    this.machineType = type;
    startGCode = startSource.copy();
    endGCode = endSource.copy();
    if (useWipes) {
      leftWipe =
          Base.getMachineLoader().getMachineInterface().getModel().getWipeFor(ToolheadAlias.LEFT);
      rightWipe =
          Base.getMachineLoader().getMachineInterface().getModel().getWipeFor(ToolheadAlias.RIGHT);

      if (leftWipe == null || rightWipe == null) {
        String error =
            "Could not find wipes for the current machine: "
                + Base.getMachineLoader().getMachineInterface().getModel().toString()
                + ". Continuing without wipes.";
        JOptionPane.showConfirmDialog(
            null,
            error,
            "Could not find wipes!",
            JOptionPane.DEFAULT_OPTION,
            JOptionPane.ERROR_MESSAGE);

        useWipes = false;
      }
    } else {
      leftWipe = null;
      rightWipe = null;
    }
  }
  /**
   * does the post-processing, called by Slic3r Generator
   *
   * @return
   */
  protected BuildCode runPostProcessing() {
    // Load our code to a source iterator
    source = new MutableGCodeSource(generator.output.file);

    if (!dualstruding) {
      if (prependStart) runPrepend(startCode);
      if (appendEnd) runAppend(endCode);

      if (!multiHead) toolheadTarget = ToolheadAlias.SINGLE;

      if (toolheadTarget != null) runToolheadSwap(toolheadTarget);
    }

    // these display the build % on The Replicator
    if (addProgressUpdates) {
      source.addSlic3rProgressUpdates();
    }

    if (prependMetaInfo) {
      MutableGCodeSource metaInfo = new MutableGCodeSource();
      String curDate = getPrettyPrintDate();
      String machineName = (machineType != null ? machineType.getName() : "CNC Machine");
      // metaInfo.add("(** UUID: " + UUID.randomUUID().toString() + " **)");
      metaInfo.add("(** This GCode was generated by ReplicatorG " + Base.VERSION_NAME + " **)");
      // TRICKY: calling a static method on an instance of a class is considered bad practice,
      //				but I'm not sure how to access displayName without it
      metaInfo.add("(*  using " + generator.displayName + "  *)");
      metaInfo.add(
          "(*  for a " + (multiHead ? "Dual headed " : "Single headed ") + machineName + "  *)");
      metaInfo.add("(*  on " + curDate + " *)");

      runPrepend(metaInfo);
    }

    // scans to cool unused head if required
    //		if( multiHead )
    //			source.coolUnusedToolhead();

    // Write the modified source back to our file
    source.writeToFile(generator.output.file);

    return generator.output;
  }
 /**
  * appends code to the file
  *
  * @param newCode
  */
 private void runAppend(GCodeSource newCode) {
   if (newCode != null) source.add(newCode);
 }
 /**
  * prepends code to the file
  *
  * @param newCode
  */
 private void runPrepend(GCodeSource newCode) {
   if (newCode != null) source.add(0, newCode);
 }
 /**
  * switches all toolhead specific code to the target toolhead
  *
  * @param switchTo
  */
 private void runToolheadSwap(ToolheadAlias switchTo) {
   System.out.println("runToolheadSwap");
   source.changeToolhead(switchTo);
 }
 /// Make a deep copy of this MutableGCodeSource and returns it to the caller.
 public MutableGCodeSource copy() {
   MutableGCodeSource newSource = new MutableGCodeSource();
   for (String line : source) newSource.add(new String(line));
   return newSource;
 }