/*.................................................................................................................*/
  public void actionPerformed(ActionEvent e) {
    if (e.getActionCommand().equalsIgnoreCase("setToDefaults")) {
      setDefaultTNTCommandsSearchOptions();
      searchField.setText(searchArguments);
      bootstrapSearchField.setText(bootstrapSearchArguments);
      harvestOnlyStrictConsensusBox.setState(harvestOnlyStrictConsensus);
      resamplingAllConsensusTreesBox.setState(!resamplingAllConsensusTrees);
      bootStrapRepsField.setValue(bootstrapreps);

    } else if (e.getActionCommand().equalsIgnoreCase("setToDefaultsOtherOptions")) {
      setDefaultTNTCommandsOtherOptions();
      otherOptionsField.setText(otherOptions);
      convertGapsBox.setState(convertGapsToMissing);
    } else if (e.getActionCommand().equalsIgnoreCase("browseSearchScript")
        && searchScriptPathField != null) {
      MesquiteString directoryName = new MesquiteString();
      MesquiteString fileName = new MesquiteString();
      String path =
          MesquiteFile.openFileDialog("Choose Search Script File", directoryName, fileName);
      if (StringUtil.notEmpty(path)) searchScriptPathField.setText(path);
    } else if (e.getActionCommand().equalsIgnoreCase("browseBootSearchScript")
        && bootSearchScriptPathField != null) {

      MesquiteString directoryName = new MesquiteString();
      MesquiteString fileName = new MesquiteString();
      String path =
          MesquiteFile.openFileDialog(
              "Choose Resampling Search Script File", directoryName, fileName);
      if (StringUtil.notEmpty(path)) bootSearchScriptPathField.setText(path);
    }
  }
  /*.................................................................................................................*/
  public void processAceFileWithContig(
      CharacterData data,
      MesquiteModule ownerModule,
      String processedAceFilePath,
      String fragmentDirPath,
      AceFile ace,
      SequenceUploader uploader,
      String geneName,
      MesquiteString fullName,
      String baseName,
      MesquiteString voucherCode,
      int it) {
    DNAData editedData = ChromaseqUtil.getEditedData(data);
    DNAData originalData = ChromaseqUtil.getOriginalData(data);
    Taxa taxa = data.getTaxa();
    ace.setNameTranslation(fileNameTranslation);
    ownerModule.log(ace.contigListForLog() + StringUtil.lineEnding());
    if (processPolymorphisms) ace.processPolys(); // creates an additional CO that has polys in it
    if (renameContigsInAceFiles) ace.renameContigs(fullName.toString(), addFragName, geneName);
    ace.setLowQualityToLowerCase(qualThresholdForLowerCase);
    ace.writeToPropertiesFile(contigPropertiesFileBuffer, fullName.toString());
    if (truncateMixedEnds)
      ace.trimMixedEnds(mixedEndThreshold, mixedEndWindow, qualThresholdForTrim, addPhrapFailures);

    /*		if (uploadResultsToDatabase && StringUtil.notEmpty(databaseURL)) {
    			uploader.uploadAceFileToServer(MesquiteXMLToLUtilities.getTOLPageDatabaseURL(databaseURL), ace, processPolymorphisms, qualThresholdForTrim);
    		}
    */
    System.out.println("\n\nfasta file name: " + baseName + " ace file: " + ace);
    MesquiteFile.putFileContents(
        fragmentDirPath
            + MesquiteFile.fileSeparator
            + ChromaseqUtil.processedFastaFolder
            + MesquiteFile.fileSeparator
            + baseName
            + ".fas",
        ace.toFASTAString(processPolymorphisms, qualThresholdForTrim),
        true);
    MesquiteFile.putFileContents(processedAceFilePath, ace.toString(processPolymorphisms), true);
    ace.importSequence(
        taxa,
        editedData,
        it,
        originalData,
        ChromaseqUtil.getQualityData(data),
        ChromaseqUtil.getRegistryData(data),
        singleTaxaBlock,
        processPolymorphisms,
        maxChar,
        " contig ",
        false,
        voucherCode);
  }
  /*.................................................................................................................*/
  public void processAceFileWithoutContig(
      DNAData data,
      String processedAceFilePath,
      AceFile ace,
      String geneName,
      MesquiteString fullName,
      int it,
      MesquiteString voucherCode) {
    DNAData editedData = ChromaseqUtil.getEditedData(data);
    DNAData originalData = ChromaseqUtil.getOriginalData(data);
    Taxa taxa = data.getTaxa();
    ace.processFailedContig(polyThreshold);
    ace.setNameTranslation(fileNameTranslation);

    ace.renameContigs(fullName.toString(), addFragName, geneName);
    ace.setLowQualityToLowerCase(qualThresholdForLowerCase);
    ace.writeToPropertiesFile(contigPropertiesFileBuffer, fullName.toString());
    if (truncateMixedEnds) {
      ace.trimMixedEnds(mixedEndThreshold, mixedEndWindow, qualThresholdForTrim, addPhrapFailures);
    }
    MesquiteFile.putFileContents(processedAceFilePath, ace.toString(processPolymorphisms), true);
    ace.importSequence(
        taxa,
        editedData,
        it,
        originalData,
        ChromaseqUtil.getQualityData(data),
        ChromaseqUtil.getRegistryData(data),
        singleTaxaBlock,
        processPolymorphisms,
        maxChar,
        "",
        true,
        voucherCode);
  }
  /** monitors the run. */
  public boolean monitorAndCleanUpShell() {
    lastModified = null;
    boolean stillGoing = true;
    if (outputFilePaths != null) {
      lastModified = new long[outputFilePaths.length];
      LongArray.deassignArray(lastModified);
    }

    if (!StringUtil.blank(
        runningFilePath)) // is file at runningFilePath; watch for its disappearance
    while (MesquiteFile.fileExists(runningFilePath) && stillGoing) {
        processOutputFiles();
        try {
          Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
          MesquiteMessage.notifyProgrammer(
              "InterruptedException in shell script executed by " + name);
          return false;
        }
        stillGoing = watcher == null || watcher.continueShellProcess(proc);
      }

    if (outputFileProcessor != null)
      outputFileProcessor.processCompletedOutputFiles(outputFilePaths);
    return true;
  }
  /*................................................................................................*/
  public boolean startJob(String arguments, Object condition, boolean hiredByName) {
    addMenuItem("Directory for Sample Trees From Directory...", makeCommand("setDirPath", this));
    fillerTasks = new Vector();
    if (!MesquiteThread
        .isScripting()) { // enclosed in conditional to avoid hiring query when opening file; should
      // be handled by snapshot/doCommand when file is opened.
      String directoryPath =
          MesquiteFile.chooseDirectory(
              "Choose directory containing tree files:",
              previousDirectory); // MesquiteFile.saveFileAsDialog("Base name for files (files will
      // be named <name>1.nex, <name>2.nex, etc.)", baseName);

      if (StringUtil.blank(directoryPath)) { // TODO: clean this up?
        return false;
      } else {
        directory = new File(directoryPath);
        previousDirectory = directory.getParent();
        if (directory.exists() && directory.isDirectory()) {
          return hireFillers(directoryPath);
        }
      }
    }
    return true; // Add something to make sure directory paths are set up correctly, when not
    // scripting...
  }
  /**
   * executes a shell script at "scriptPath". If runningFilePath is not blank and not null, then
   * Mesquite will create a file there that will serve as a flag to Mesquite that the script is
   * running.
   */
  public boolean executeInShell() {
    proc = null;
    try {
      ShellScriptUtil.setScriptFileToBeExecutable(scriptPath);
      if (!StringUtil.blank(runningFilePath)) {
        if (StringUtil.blank(runningFileMessage))
          MesquiteFile.putFileContents(runningFilePath, "Script running...", true);
        else MesquiteFile.putFileContents(runningFilePath, runningFileMessage, true);
        if (appendRemoveCommand && MesquiteFile.fileExists(runningFilePath))
          MesquiteFile.appendFileContents(
              scriptPath,
              StringUtil.lineEnding() + ShellScriptUtil.getRemoveCommand(runningFilePath),
              true); // append remove command to guarantee that the runningFile is deleted
        // +StringUtil.lineEnding()+ShellScriptUtil.getExitCommand()
      }
      proc = ShellScriptUtil.executeScript(scriptPath, visibleTerminal);

    } catch (IOException e) {
      MesquiteMessage.warnProgrammer("IOException in shell script executed by " + name);
      return false;
    }
    return true;
  }
  /*.................................................................................................................*/
  public void reprocessAceFileDirectory(
      MesquiteFile file, MesquiteModule ownerModule, DNAData data, int it) {
    if (data == null || file == null) return;
    String aceFileDirectoryPath =
        ChromaseqUtil.getAceFileDirectory(file.getDirectoryName(), ownerModule, data, it);
    File aceFileDirectory = new File(aceFileDirectoryPath);
    boolean addFragName = false; // control of this?
    int currentRead = -1;
    String dataFilePath = MesquiteFile.composePath(data.getProject().getHomeDirectoryName(), "");
    boolean addingPhrapFailures = false;
    AceFile ace = null;
    MesquiteProject project = data.getProject();
    if (project == null) return;
    String processedAceFilePath = "";
    MesquiteString fullName = null;
    MesquiteString voucherCode = null;
    String geneName = ChromaseqUtil.getGeneName(data);
    if (aceFileDirectory.isDirectory()) {
      int numPhdFiles = getNumPhdFilesInDirectory(aceFileDirectory, aceFileDirectoryPath);
      fileNameTranslation = new String[5][numPhdFiles];
      fillNameTranslation(data, it, numPhdFiles);

      String[] files = aceFileDirectory.list();
      for (int i = 0;
          i < files.length;
          i++) { // going through the folders and finding the ace files
        if (files[i] != null) {
          String filePath = aceFileDirectoryPath + MesquiteFile.fileSeparator + files[i];
          String infoFilePath =
              aceFileDirectoryPath + MesquiteFile.fileSeparator + ChromaseqUtil.infoFileName;
          File cFile = new File(filePath);
          if (cFile.exists()) {
            if (!cFile.isDirectory()) {
              if (files[i].endsWith(ChromaseqUtil.processedACESuffix + ".ace")) {
                // don't do anything
              } else if (files[i].endsWith(".ace")
                  && !files[i].startsWith(".")
                  && !addingPhrapFailures) {
                ownerModule.logln("Processing ACE file: " + files[i]);

                String baseName =
                    files[i].substring(
                        0, files[i].length() - 4); // this is the name of the sequence
                processedAceFilePath =
                    aceFileDirectoryPath
                        + MesquiteFile.fileSeparator
                        + baseName
                        + ChromaseqUtil.processedACESuffix
                        + ".ace";

                ace =
                    new AceFile(
                        filePath,
                        processedAceFilePath,
                        dataFilePath,
                        dataFilePath,
                        ownerModule,
                        processPolymorphisms,
                        polyThreshold,
                        false);
                if (ace == null) return;

                ace.setBaseName(baseName);
                fullName = new MesquiteString(baseName);
                voucherCode = new MesquiteString();
                ChromaseqInfoFile.processInfoFile(infoFilePath, fullName, voucherCode);
                String fragmentDirPath =
                    StringUtil.getAllButLastItem(
                        StringUtil.getAllButLastItem(
                            aceFileDirectoryPath, MesquiteFile.fileSeparator),
                        MesquiteFile.fileSeparator);
                ace.setLongSequenceName(fullName.toString());
                if (ace.getNumContigs() >= 1) {
                  processAceFileWithContig(
                      data,
                      ownerModule,
                      processedAceFilePath,
                      fragmentDirPath,
                      ace,
                      null,
                      geneName,
                      fullName,
                      baseName,
                      voucherCode,
                      it);
                } else {
                  ownerModule.logln("   ACE file contains no contigs!");
                  if (project != null) {
                    addingPhrapFailures = true;
                    i = 0;
                    ace.createEmptyContigs(
                        MesquiteFile.numFilesEndingWith(
                            aceFileDirectoryPath, files, ".phd.1")); // create an empty contig
                    ace.renameContigs(fullName.toString(), addFragName, geneName);
                  }
                }

                if (!addingPhrapFailures) ace.dispose();
              } else if (files[i].endsWith(".phd.1") && addingPhrapFailures) {
                ownerModule.logln("   Importing single-read Phred file " + files[i]);
                currentRead++;
                ace.addPhdFileAsSingleReadInContig(
                    currentRead,
                    aceFileDirectoryPath,
                    files[i],
                    processPolymorphisms,
                    polyThreshold);
              }
            }
          }
        }
      }
    }

    if (addingPhrapFailures && ace != null) { // have to process AceFile that we have manually made
      MesquiteFile.putFileContents(processedAceFilePath, ace.toString(processPolymorphisms), true);
      if (project != null) {
        processAceFileWithoutContig(
            data, processedAceFilePath, ace, geneName, fullName, it, voucherCode);
      }
      ace.dispose();
    }
  }
  public void runFilesAvailable(int fileNum) {
    String[] logFileNames = getLogFileNames();
    if ((progIndicator != null && progIndicator.isAborted()) || logFileNames == null) return;
    String[] outputFilePaths = new String[logFileNames.length];
    outputFilePaths[fileNum] = externalProcRunner.getOutputFilePath(logFileNames[fileNum]);
    String filePath = outputFilePaths[fileNum];

    if (fileNum == 0
        && outputFilePaths.length > 0
        && !StringUtil.blank(outputFilePaths[0])
        && !bootstrapOrJackknife()) { // tree file
      if (ownerModule instanceof NewTreeProcessor) {
        String treeFilePath = filePath;
        if (taxa != null) {
          TaxaSelectionSet outgroupSet =
              (TaxaSelectionSet) taxa.getSpecsSet(outgroupTaxSetString, TaxaSelectionSet.class);
          ((NewTreeProcessor) ownerModule).newTreeAvailable(treeFilePath, outgroupSet);

        } else ((NewTreeProcessor) ownerModule).newTreeAvailable(treeFilePath, null);
      }
    } else if (fileNum == 1
        && outputFilePaths.length > 1
        && !StringUtil.blank(outputFilePaths[1])
        && !bootstrapOrJackknife()) { // log file
      if (MesquiteFile.fileExists(filePath)) {
        String s = MesquiteFile.getFileLastContents(filePath);
        if (!StringUtil.blank(s))
          if (progIndicator != null) {
            parser.setString(s);
            String rep = parser.getFirstToken(); // generation number
            logln("");
            if (MesquiteInteger.isNumber(rep)) {
              int numReps = MesquiteInteger.fromString(rep) + 1;
              progIndicator.setText(
                  "Replicate: " + numReps); // + ", ln L = " + parser.getNextToken());
              if (bootstrapOrJackknife()) {
                logln("Replicate " + numReps + " of " + bootstrapreps);
              }
              logln("Replicate " + numReps + " of " + totalNumHits);

              progIndicator.spin();
              double timePerRep = 0;
              if (MesquiteInteger.isCombinable(numReps) && numReps > 0) {
                timePerRep = timer.timeSinceVeryStartInSeconds() / numReps; // this is time per rep
              }
              int timeLeft = 0;
              if (bootstrapOrJackknife()) {
                timeLeft = (int) ((bootstrapreps - numReps) * timePerRep);
              } else {
                String token = parser.getNextToken(); // algorithm
                token = parser.getNextToken(); // Tree
                token = parser.getNextToken(); // Score
                String best = parser.getNextToken(); // Best
                logln("  Score " + token + "; best found so far " + best);
                timeLeft = (int) ((totalNumHits - numReps) * timePerRep);
              }

              logln(
                  "  Running time so far "
                      + StringUtil.secondsToHHMMSS((int) timer.timeSinceVeryStartInSeconds())
                      + ", approximate time remaining "
                      + StringUtil.secondsToHHMMSS(timeLeft));
            }
          }
        count++;
      } else if (MesquiteTrunk.debugMode) logln("*** File does not exist (" + filePath + ") ***");
    }
  }
  /*.................................................................................................................*/
  public Tree getTrees(
      TreeVector trees,
      Taxa taxa,
      MCharactersDistribution matrix,
      long seed,
      MesquiteDouble finalScore) {
    if (!initializeGetTrees(CategoricalData.class, taxa, matrix)) return null;
    setTNTSeed(seed);
    isProtein = data instanceof ProteinData;

    // David: if isDoomed() then module is closing down; abort somehow

    // write data file
    String tempDir =
        MesquiteFileUtil.createDirectoryForFiles(
            this, MesquiteFileUtil.IN_SUPPORT_DIR, "TNT", "-Run.");
    if (tempDir == null) return null;
    String dataFileName = "data.ss"; // replace this with actual file name?
    String dataFilePath = tempDir + dataFileName;

    FileInterpreterI exporter = ZephyrUtil.getFileInterpreter(this, "#InterpretTNT");
    if (exporter == null) return null;
    boolean fileSaved = false;
    String translationTable = namer.getTranslationTable(taxa);
    ((InterpretHennig86Base) exporter).setTaxonNamer(namer);

    fileSaved = ZephyrUtil.saveExportFile(this, exporter, dataFilePath, data, selectedTaxaOnly);
    if (!fileSaved) return null;

    String translationFileName = IOUtil.translationTableFileName;
    setTaxonTranslation(taxa);
    taxonNumberTranslation = getTaxonNumberTranslation(taxa);
    namer.setNumberTranslationTable(taxonNumberTranslation);

    setFileNames();

    TaxaSelectionSet outgroupSet =
        (TaxaSelectionSet) taxa.getSpecsSet(outgroupTaxSetString, TaxaSelectionSet.class);
    int firstOutgroup = MesquiteInteger.unassigned;
    if (outgroupSet != null) firstOutgroup = outgroupSet.firstBitOn();
    formCommandFile(dataFileName, firstOutgroup);
    logln("\n\nCommands given to TNT:");
    logln(commands);
    logln("");

    MesquiteString arguments = new MesquiteString();
    arguments.setValue(" proc " + commandsFileName);

    String programCommand = externalProcRunner.getExecutableCommand();

    int numInputFiles = 3;
    String[] fileContents = new String[numInputFiles];
    String[] fileNames = new String[numInputFiles];
    for (int i = 0; i < numInputFiles; i++) {
      fileContents[i] = "";
      fileNames[i] = "";
    }
    fileContents[0] = MesquiteFile.getFileContentsAsString(dataFilePath);
    fileNames[0] = dataFileName;
    fileContents[1] = commands;
    fileNames[1] = commandsFileName;
    fileContents[2] = translationTable;
    fileNames[2] = translationFileName;

    // ----------//
    boolean success =
        runProgramOnExternalProcess(
            programCommand, arguments, fileContents, fileNames, ownerModule.getName());

    if (!isDoomed()) {
      if (success) {
        desuppressProjectPanelReset();
        return retrieveTreeBlock(trees, finalScore); // here's where we actually process everything.
      } else {
        if (!beanWritten) postBean("unsuccessful [1]", false);
        beanWritten = true;
      }
    }
    desuppressProjectPanelReset();
    if (data == null) data.decrementEditInhibition();
    externalProcRunner.finalCleanup();
    return null;
  }
  /*.................................................................................................................*/
  void formCommandFile(String dataFileName, int firstOutgroup) {
    if (parallel) {
      commands = "";
    }
    commands += getTNTCommand("mxram " + mxram);

    commands += getTNTCommand("report+0/1/0");
    commands += getTNTCommand("log " + logFileName);
    commands += getTNTCommand("p " + dataFileName);
    commands += getTNTCommand("vversion");
    if (MesquiteInteger.isCombinable(firstOutgroup) && firstOutgroup >= 0)
      commands += getTNTCommand("outgroup " + firstOutgroup);
    if (bootstrapOrJackknife()) {
      if (parallel) {
        commands += indentTNTCommand("ptnt begin parallelRun " + numSlaves + "/ram x 2 = ");
      }
      if (StringUtil.notEmpty(bootSearchScriptPath)) {
        String script = MesquiteFile.getFileContentsAsString(bootSearchScriptPath);
        if (StringUtil.notEmpty(script)) commands += script;
      } else
        commands += StringUtil.lineEnding() + bootstrapSearchArguments + StringUtil.lineEnding();
      String saveTreesString = "";
      if (resamplingAllConsensusTrees) saveTreesString = " savetrees ";
      String bootSearchString = " [xmult; bb]";
      bootSearchString = "";

      if (parallel) {
        int numRepsPerSlave = bootstrapreps / numSlaves;
        if (numRepsPerSlave * numSlaves < bootstrapreps) numRepsPerSlave++;
        if (searchStyle == BOOTSTRAPSEARCH)
          commands +=
              getTNTCommand(
                  "resample boot cut 50 "
                      + saveTreesString
                      + " replications "
                      + numRepsPerSlave
                      + " [xmult; bb] savetrees"); // + getComDelim();
        else if (searchStyle == JACKKNIFESEARCH)
          commands +=
              getTNTCommand(
                  "resample jak cut 50 "
                      + saveTreesString
                      + " replications "
                      + numRepsPerSlave
                      + " [xmult; bb] savetrees"); // + getComDelim();
        else if (searchStyle == SYMSEARCH)
          commands +=
              getTNTCommand(
                  "resample sym cut 50 "
                      + saveTreesString
                      + " replications "
                      + numRepsPerSlave
                      + " [xmult; bb] savetrees"); // + getComDelim();
        else if (searchStyle == POISSONSEARCH)
          commands +=
              getTNTCommand(
                  "resample poisson cut 50 "
                      + saveTreesString
                      + " replications "
                      + numRepsPerSlave
                      + " [xmult; bb] savetrees"); // + getComDelim();
        commands += getTNTCommand("return");
        commands += getTNTCommand("ptnt wait parallelRun");
        commands += getTNTCommand("ptnt get parallelRun");
      } else {
        if (!resamplingAllConsensusTrees) {
          commands += getTNTCommand("macro=");
          commands += getTNTCommand("ttags =");
        }
        commands += getTNTCommand("tsave *" + treeFileName);
        if (bootstrapAllowed) {
          if (searchStyle == BOOTSTRAPSEARCH)
            commands +=
                getTNTCommand(
                    "resample boot "
                        + saveTreesString
                        + " replications "
                        + bootstrapreps
                        + bootSearchString); // + getComDelim();
          else if (searchStyle == JACKKNIFESEARCH)
            commands +=
                getTNTCommand(
                    "resample jak cut 50 "
                        + saveTreesString
                        + " replications "
                        + bootstrapreps
                        + bootSearchString); // + getComDelim();
          else if (searchStyle == SYMSEARCH)
            commands +=
                getTNTCommand(
                    "resample sym cut 50 "
                        + saveTreesString
                        + " replications "
                        + bootstrapreps
                        + bootSearchString); // + getComDelim();
          else if (searchStyle == POISSONSEARCH)
            commands +=
                getTNTCommand(
                    "resample poisson cut 50 "
                        + saveTreesString
                        + " replications "
                        + bootstrapreps
                        + bootSearchString); // + getComDelim();
        }
        if (!resamplingAllConsensusTrees) commands += getTNTCommand("save *");
        else commands += getTNTCommand("save");
        commands += getTNTCommand("tsave/");
        if (!resamplingAllConsensusTrees) {
          commands += getTNTCommand("ttags -/");
          commands += getTNTCommand("macro-");
        }
      }

      // commands += getTNTCommand("proc/") ;

      commands += getTNTCommand("log/");

      //	if (!parallel)
      commands += getTNTCommand("quit");
    } else {
      // commands += getTNTCommand("tsave !5 " + treeFileName) ;   // if showing intermediate trees
      commands += getTNTCommand("tsave *" + treeFileName);
      if (StringUtil.notEmpty(searchScriptPath)) {
        String script = MesquiteFile.getFileContentsAsString(searchScriptPath);
        if (StringUtil.notEmpty(script)) commands += script;
      } else commands += searchArguments;
      commands += otherOptions;
      if (harvestOnlyStrictConsensus) commands += getTNTCommand("nelsen *");
      commands += getTNTCommand("save");
      commands += getTNTCommand("log/");

      commands += getTNTCommand("tsave/");
      commands += getTNTCommand("quit");
    }
  }