/**
   * Creates the Andromeda PTM file and saves the PTM indexes in the search parameters.
   *
   * @param andromedaFolder the Andromeda installation folder
   * @param identificationParameters the identification parameters
   * @param identificationParametersFile the file where to save the search parameters
   * @throws IOException exception thrown whenever an error occurs while writing to the file.
   * @throws java.lang.ClassNotFoundException exception thrown whenever an error occurred while
   *     saving the search parameters
   */
  public static void createPtmFile(
      File andromedaFolder,
      IdentificationParameters identificationParameters,
      File identificationParametersFile)
      throws IOException, ClassNotFoundException {

    File file = new File(andromedaFolder, "conf");
    file = new File(file, "modifications.xml");
    BufferedWriter bw = new BufferedWriter(new FileWriter(file));
    int index = 0;
    String date = "0001-01-01T00:00:00";

    PTMFactory ptmFactory = PTMFactory.getInstance();
    SearchParameters searchParameters = identificationParameters.getSearchParameters();
    AndromedaParameters andromedaParameters =
        (AndromedaParameters)
            searchParameters.getIdentificationAlgorithmParameter(Advocate.andromeda.getIndex());

    try {
      bw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
      bw.newLine();
      bw.write(
          "<modifications xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
      bw.newLine();

      // add the default ptms
      for (String ptmName : ptmFactory.getDefaultModifications()) {
        PTM ptm = ptmFactory.getPTM(ptmName);
        writePtm(bw, date, ptm, index);
        andromedaParameters.setPtmIndex(ptmName, index);
        index++;
      }

      // add the user ptms
      for (String ptmName : ptmFactory.getUserModifications()) {
        PTM ptm = ptmFactory.getPTM(ptmName);
        writePtm(bw, date, ptm, index);
        andromedaParameters.setPtmIndex(ptmName, index);
        index++;
      }

      bw.write("</modifications>");
      bw.newLine();
    } finally {
      bw.close();
    }

    IdentificationParameters.saveIdentificationParameters(
        identificationParameters, identificationParametersFile);
  }
  /**
   * Get the terminal modifications as a string in the Tide format.
   *
   * @param modifications the modifications to check
   * @param fixed if the modifications are to to be added as fixed or variable
   * @param nTerm true if the modifications are n-terminal, false if c-terminal
   * @return the terminal modifications as a string in the Tide format
   */
  private String getTerminalModifications(
      ArrayList<String> modifications, boolean fixed, boolean nTerm) {

    String terminalModifications = "";

    for (String ptmName : modifications) {

      PTM ptm = ptmFactory.getPTM(ptmName);

      if ((ptm.isNTerm() && nTerm) || (ptm.isCTerm() && !nTerm)) {

        if (!terminalModifications.isEmpty()) {
          terminalModifications += ",";
        }

        // add the number of allowed ptms per peptide
        if (!fixed) {
          terminalModifications += "1";
        }

        // add the residues affected
        AminoAcidPattern ptmPattern = ptm.getPattern();
        String tempPtmPattern = "";
        if (ptmPattern != null && ptmPattern.length() > 0) {
          for (Character aminoAcid : ptmPattern.getAminoAcidsAtTarget()) {
            tempPtmPattern += aminoAcid;
          }
        }

        if (tempPtmPattern.length() == 0) {
          tempPtmPattern = "X";
        }

        terminalModifications += tempPtmPattern;

        // add the ptm mass
        if (ptm.getRoundedMass() > 0) {
          terminalModifications += "+";
        }
        terminalModifications += ptm.getRoundedMass();
      }
    }

    return terminalModifications;
  }
  /**
   * Get the non-terminal modifications as a string in the Tide format.
   *
   * @param modifications the modifications to check
   * @param fixed if the modifications are to to be added as fixed or variable
   * @return the non-terminal modifications as a string in the Tide forma
   */
  private String getNonTerminalModifications(ArrayList<String> modifications, boolean fixed) {

    // tide ptm pattern: [max_per_peptide]residues[+/-]mass_change
    String nonTerminalModifications = "";

    for (String ptmName : modifications) {

      PTM ptm = ptmFactory.getPTM(ptmName);

      if (!ptm.isNTerm() && !ptm.isCTerm()) {

        if (!nonTerminalModifications.isEmpty()) {
          nonTerminalModifications += ",";
        }

        // add the number of allowed ptms per peptide
        if (!fixed) {
          nonTerminalModifications +=
              tideParameters
                  .getMaxVariablePtmsPerTypePerPeptide(); // @TODO: make this modification specific?
        }

        // add the residues affected
        AminoAcidPattern ptmPattern = ptm.getPattern();
        if (ptmPattern != null && ptmPattern.length() > 0) {
          for (Character aminoAcid : ptmPattern.getAminoAcidsAtTarget()) {
            nonTerminalModifications += aminoAcid;
          }
        }

        // add the ptm mass
        if (ptm.getRoundedMass() > 0) {
          nonTerminalModifications += "+";
        }
        nonTerminalModifications += ptm.getRoundedMass();
      }
    }

    return nonTerminalModifications;
  }
  /**
   * Writes the lines corresponding to the given PTM name.
   *
   * @param writer the writer used to write
   * @param mod the name of the modifications of interest
   * @param variable a string (see static fields) indicating whether the modification is fixed or
   *     variable
   * @throws IOException
   */
  private static void writePtmLine(BufferedWriter writer, String mod, String variable)
      throws IOException {

    // Get the PTMFactory
    PTMFactory ptmFactory = PTMFactory.getInstance();
    PTM ptm = ptmFactory.getPTM(mod);
    double ptmMass = ptm.getRoundedMass();

    if (ptmMass > maxMassOffsetValue) {

      String connector = "";
      if (ptmMass > 0) {
        connector = "+";
      }

      // Write a line for each residue
      if (ptm.getPattern() == null || ptm.getPattern().getAminoAcidsAtTarget().isEmpty()) {

        if (variable.equalsIgnoreCase(FIXED_PTM)) {
          variable = VARIABLE_PTM; // PepNovo+ does not support fixed PTMs at the terminals...
        }

        if (ptmFactory.getPTM(mod).getType() == PTM.MODN
            || ptmFactory.getPTM(mod).getType() == PTM.MODNAA
            || ptmFactory.getPTM(mod).getType() == PTM.MODNP
            || ptmFactory.getPTM(mod).getType() == PTM.MODNPAA) {
          writer.append("N_TERM" + SEP);
          writer.append(ptmMass + SPACE);
          writer.append(variable + SPACE);
          writer.append("N_TERM" + SPACE);
          writer.append("^" + connector + Long.toString(Math.round(ptmMass)) + SPACE);
          modIdMap.put(ptm.getName(), "^" + connector + Long.toString(Math.round(ptmMass)));
          writer.append(ptm.getName().toUpperCase());
          writer.newLine();
        } else if (ptmFactory.getPTM(mod).getType() == PTM.MODC
            || ptmFactory.getPTM(mod).getType() == PTM.MODCAA
            || ptmFactory.getPTM(mod).getType() == PTM.MODCP
            || ptmFactory.getPTM(mod).getType() == PTM.MODCPAA) {
          writer.append("C_TERM" + SEP);
          writer.append(ptmMass + SPACE);
          writer.append(variable + SPACE);
          writer.append("C_TERM" + SPACE);
          writer.append("$" + connector + Long.toString(Math.round(ptmMass)) + SPACE);
          modIdMap.put(ptm.getName(), "$" + connector + Long.toString(Math.round(ptmMass)));
          writer.append(ptm.getName().toUpperCase());
          writer.newLine();
        }
      } else {

        for (Character residue : ptm.getPattern().getAminoAcidsAtTarget()) {

          if (ptmFactory.getPTM(mod).getType() == PTM.MODN
              || ptmFactory.getPTM(mod).getType() == PTM.MODNAA
              || ptmFactory.getPTM(mod).getType() == PTM.MODNP
              || ptmFactory.getPTM(mod).getType() == PTM.MODNPAA) {
            writer.append(residue + SEP);
            writer.append(ptmMass + SPACE);
            writer.append(variable + SPACE);
            writer.append("+1" + SPACE);
            writer.append(residue + connector + Long.toString(Math.round(ptmMass)) + SPACE);
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          } else if (ptmFactory.getPTM(mod).getType() == PTM.MODC
              || ptmFactory.getPTM(mod).getType() == PTM.MODCAA
              || ptmFactory.getPTM(mod).getType() == PTM.MODCP
              || ptmFactory.getPTM(mod).getType() == PTM.MODCPAA) {
            writer.append(residue + SEP);
            writer.append(ptmMass + SPACE);
            writer.append(variable + SPACE);
            writer.append("-1" + SPACE);
            writer.append(residue + connector + Long.toString(Math.round(ptmMass)) + SPACE);
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          } else {
            writer.append(residue + SEP);
            writer.append(ptmMass + SPACE);
            writer.append(variable + SPACE);
            writer.append(ALL_LOCATIONS + SPACE);
            writer.append(residue + connector + Long.toString(Math.round(ptmMass)) + SPACE);
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          }

          writer.append(ptm.getName().toUpperCase());
          writer.newLine();
        }
      }
    }
  }
  /** Fill the modification ID map. */
  private static void fillModIdMap() {

    // @TODO: is this method really needed anymore?
    modIdMap = new HashMap<String, String>();
    PTMFactory ptmFactory = PTMFactory.getInstance();
    List<String> mods = new ArrayList<String>();
    mods.addAll(ptmFactory.getDefaultModifications());
    mods.addAll(ptmFactory.getUserModifications());
    // Connector string: plus for positive modifications, minus for negative ones
    String connector;

    // Write the modifications
    for (String mod : mods) {
      PTM ptm = ptmFactory.getPTM(mod);
      double ptmMass = ptm.getRoundedMass();

      if (ptmMass > 0) {
        connector = "+";
      } else {
        connector = "";
      }

      if (ptm.getPattern() == null || ptm.getPattern().getAminoAcidsAtTarget().isEmpty()) {
        if (ptmFactory.getPTM(mod).getType() == PTM.MODN
            || ptmFactory.getPTM(mod).getType() == PTM.MODNAA
            || ptmFactory.getPTM(mod).getType() == PTM.MODNP
            || ptmFactory.getPTM(mod).getType() == PTM.MODNPAA) {
          modIdMap.put(ptm.getName(), "^" + connector + Long.toString(Math.round(ptmMass)));
        } else if (ptmFactory.getPTM(mod).getType() == PTM.MODC
            || ptmFactory.getPTM(mod).getType() == PTM.MODCAA
            || ptmFactory.getPTM(mod).getType() == PTM.MODCP
            || ptmFactory.getPTM(mod).getType() == PTM.MODCPAA) {
          modIdMap.put(ptm.getName(), "$" + connector + Long.toString(Math.round(ptmMass)));
        }
      } else {
        for (Character residue : ptmFactory.getPTM(mod).getPattern().getAminoAcidsAtTarget()) {
          if (ptmFactory.getPTM(mod).getType() == PTM.MODN
              || ptmFactory.getPTM(mod).getType() == PTM.MODNAA
              || ptmFactory.getPTM(mod).getType() == PTM.MODNP
              || ptmFactory.getPTM(mod).getType() == PTM.MODNPAA) {
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          } else if (ptmFactory.getPTM(mod).getType() == PTM.MODC
              || ptmFactory.getPTM(mod).getType() == PTM.MODCAA
              || ptmFactory.getPTM(mod).getType() == PTM.MODCP
              || ptmFactory.getPTM(mod).getType() == PTM.MODCPAA) {
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          } else {
            modIdMap.put(ptm.getName(), residue + connector + Long.toString(Math.round(ptmMass)));
          }
        }
      }
    }
  }