/**
   * Converts the {@link MultipleAlignment} into a FatCat String format. Includes summary
   * information about the alignment in the top and a multiple sequence alignment at the bottom.
   *
   * @param alignment MultipleAlignment
   * @return String multiple sequence alignment in FASTA format
   */
  public static String toFatCat(MultipleAlignment alignment) {

    // Initialize the String and put the summary information
    StringWriter fatcat = new StringWriter();
    fatcat.append(alignment.toString() + "\n\n");

    // Get the alignment sequences and the mapping
    List<Integer> mapSeqToStruct = new ArrayList<Integer>();
    List<String> alnSequences =
        MultipleAlignmentTools.getSequenceAlignment(alignment, mapSeqToStruct);

    // Get the String of the Block Numbers for Position
    String blockNumbers = "";
    for (int pos = 0; pos < alnSequences.get(0).length(); pos++) {
      int blockNr =
          MultipleAlignmentTools.getBlockForSequencePosition(alignment, mapSeqToStruct, pos);
      if (blockNr != -1) blockNumbers = blockNumbers.concat("" + (blockNr + 1));
      else blockNumbers = blockNumbers.concat(" ");
    }

    // Write the Sequence Alignment
    for (int str = 0; str < alignment.size(); str++) {
      if (str < 9) fatcat.append("Chain 0" + (str + 1) + ": " + alnSequences.get(str) + "\n");
      else fatcat.append("Chain " + (str + 1) + ": " + alnSequences.get(str) + "\n");
      if (str != alignment.size() - 1) fatcat.append("          " + blockNumbers + "\n");
    }
    return fatcat.toString();
  }
  /**
   * Generates a simple MultipleAlignment: 3 structures with the same Atoms but incorreclty aligned
   * with gaps.
   *
   * @return MultipleAlignment gapped MSTA
   * @throws StructureException
   */
  private MultipleAlignment gappedMSTA() throws StructureException {

    // Generate three identical Atom arrays
    List<Atom[]> atomArrays = new ArrayList<Atom[]>(30);
    for (int i = 0; i < 3; i++) atomArrays.add(makeDummyCA(30));

    // Generate alignment with nulls and some missalignments
    List<List<Integer>> alnRes = new ArrayList<List<Integer>>(3);
    List<Integer> chain1 = Arrays.asList(1, 2, 3, 5, 8, 10, 12, 15, 17, 19, 22, null, 24, 27);
    List<Integer> chain2 = Arrays.asList(1, null, 3, 6, 9, 11, 12, 15, null, 18, 22, 24, 26, 28);
    List<Integer> chain3 = Arrays.asList(1, 2, 4, 7, 9, 10, null, 15, null, 17, 22, 24, 26, 28);

    alnRes.add(chain1);
    alnRes.add(chain2);
    alnRes.add(chain3);

    // MultipleAlignment generation
    MultipleAlignment msa = new MultipleAlignmentImpl();
    msa.getEnsemble().setAtomArrays(atomArrays);
    BlockSet bs = new BlockSetImpl(msa);
    Block b = new BlockImpl(bs);
    b.setAlignRes(alnRes);

    // We want the identity transfromations to mantain the missalignments
    Matrix4d ident = new Matrix4d();
    ident.setIdentity();
    msa.setTransformations(Arrays.asList(ident, ident, ident));

    return msa;
  }
  /**
   * Generates a simple MultipleAlignment: 3 structures with the same Atoms but incorreclty aligned
   * (offset of 1 position) without gaps.
   *
   * @return MultipleAlignment simple MSTA
   * @throws StructureException
   */
  private MultipleAlignment simpleMSTA() throws StructureException {

    // Generate three identical Atom arrays
    List<Atom[]> atomArrays = new ArrayList<Atom[]>(52);
    for (int i = 0; i < 3; i++) atomArrays.add(makeDummyCA(52));

    // Generate the incorrect alignment (0-1-2,1-2-3,etc)
    List<List<Integer>> alnRes = new ArrayList<List<Integer>>(3);
    for (int str = 0; str < 3; str++) {
      List<Integer> chain = new ArrayList<Integer>(50);
      for (int res = 0; res < 50; res++) chain.add(res + str);
      alnRes.add(chain);
    }

    // MultipleAlignment generation
    MultipleAlignment msa = new MultipleAlignmentImpl();
    msa.getEnsemble().setAtomArrays(atomArrays);
    BlockSet bs = new BlockSetImpl(msa);
    Block b = new BlockImpl(bs);
    b.setAlignRes(alnRes);

    // We want the identity transfromations to maintain the missalignment
    Matrix4d ident = new Matrix4d();
    ident.setIdentity();
    msa.setTransformations(Arrays.asList(ident, ident, ident));

    return msa;
  }
  /**
   * Generates an identity MultipleAlignment: 3 structures with the same Atoms and perfectly
   * aligned, so that TM-score = 1 and RMSD = 0.
   *
   * @return MultipleAlignment identity
   * @throws StructureException
   */
  private MultipleAlignment identityMSTA() throws StructureException {

    // Generate the identical Atom arrays
    List<Atom[]> atomArrays = new ArrayList<Atom[]>(20);
    for (int i = 0; i < 3; i++) atomArrays.add(makeDummyCA(20));

    // Generate the identity alignment (1-1-1,2-2-2,etc)
    List<List<Integer>> alnRes = new ArrayList<List<Integer>>(3);
    for (int str = 0; str < 3; str++) {
      List<Integer> chain = new ArrayList<Integer>(20);
      for (int res = 0; res < 20; res++) chain.add(res);
      alnRes.add(chain);
    }

    // MultipleAlignment generation
    MultipleAlignment msa = new MultipleAlignmentImpl();
    msa.getEnsemble().setAtomArrays(atomArrays);
    BlockSet bs = new BlockSetImpl(msa);
    Block b = new BlockImpl(bs);
    b.setAlignRes(alnRes);

    // Superimpose the alignment (which should give the identity matrices)
    ReferenceSuperimposer imposer = new ReferenceSuperimposer();
    imposer.superimpose(msa);

    return msa;
  }
  /**
   * Converts the {@link MultipleAlignment} into a multiple sequence alignment String in FASTA
   * format.
   *
   * @param alignment MultipleAlignment
   * @return String multiple sequence alignment in FASTA format
   */
  public static String toFASTA(MultipleAlignment alignment) {

    // Get the alignment sequences
    List<String> alnSequences = MultipleAlignmentTools.getSequenceAlignment(alignment);

    String fasta = "";
    for (int st = 0; st < alignment.size(); st++) {
      // Add the structure identifier as the head of the FASTA
      fasta +=
          ">"
              + alignment.getEnsemble().getStructureNames().get(st)
              + "\n"
              + alnSequences.get(st)
              + "\n";
    }
    return fasta;
  }
  /**
   * Converts the alignment to its simplest form: a list of groups of aligned residues. Format is
   * one line per residue group, tab delimited:
   *
   * <ul>
   *   <li>PDB number (includes insertion code)
   *   <li>Chain
   *   <li>Amino Acid (three letter code)
   * </ul>
   *
   * Example: <code>52	A	ALA	102	A	VAL	154	A	THR</code>
   *
   * <p>Note that this format loses information about blocks.
   *
   * @param multAln MultipleAlignment object
   * @return a String representation of the aligned residues.
   */
  public static String toAlignedResidues(MultipleAlignment multAln) {
    StringWriter residueGroup = new StringWriter();

    // Write structure names & PDB codes
    for (int str = 0; str < multAln.size(); str++) {
      residueGroup.append("#Struct" + (str + 1) + ":\t");
      residueGroup.append(multAln.getEnsemble().getStructureNames().get(str));
      residueGroup.append("\n");
    }
    // Whrite header for columns
    for (int str = 0; str < multAln.size(); str++)
      residueGroup.append("#Num" + (str + 1) + "\tChain" + (str + 1) + "\tAA" + (str + 1) + "\t");
    residueGroup.append("\n");

    // Write optimally aligned pairs
    for (Block b : multAln.getBlocks()) {
      for (int res = 0; res < b.length(); res++) {
        for (int str = 0; str < multAln.size(); str++) {
          Integer residue = b.getAlignRes().get(str).get(res);
          if (residue == null) {
            residueGroup.append("-");
            residueGroup.append('\t');
            residueGroup.append("-");
            residueGroup.append('\t');
            residueGroup.append("-");
            residueGroup.append('\t');
          } else {
            Atom atom = multAln.getEnsemble().getAtomArrays().get(str)[residue];

            residueGroup.append(atom.getGroup().getResidueNumber().toString());
            residueGroup.append('\t');
            residueGroup.append(atom.getGroup().getChain().getChainID());
            residueGroup.append('\t');
            residueGroup.append(atom.getGroup().getPDBName());
            residueGroup.append('\t');
          }
        }
        residueGroup.append('\n');
      }
    }
    return residueGroup.toString();
  }
  /**
   * Converts the transformation Matrices of the alignment into a String output.
   *
   * @param afpChain
   * @return String transformation Matrices
   */
  public static String toTransformMatrices(MultipleAlignment alignment) {

    StringBuffer txt = new StringBuffer();
    List<Matrix4d> transforms = alignment.getTransformations();

    for (int bs = 0; bs < alignment.getBlockSets().size(); bs++) {

      List<Matrix4d> btransforms = alignment.getBlockSets().get(bs).getTransformations();
      if (btransforms == null && bs == 0) btransforms = transforms;
      if (btransforms == null || btransforms.size() < 1) continue;

      if (alignment.getBlockSets().size() > 1) {
        txt.append("Operations for block ");
        txt.append(bs + 1);
        txt.append("\n");
      }

      for (int str = 0; str < alignment.size(); str++) {
        String origString = "ref";

        txt.append(
            String.format(
                "     X"
                    + (str + 1)
                    + " = (%9.6f)*X"
                    + origString
                    + " + (%9.6f)*Y"
                    + origString
                    + " + (%9.6f)*Z"
                    + origString
                    + " + (%12.6f)",
                btransforms.get(str).getElement(0, 0),
                btransforms.get(str).getElement(0, 1),
                btransforms.get(str).getElement(0, 2),
                btransforms.get(str).getElement(0, 3)));
        txt.append("\n");
        txt.append(
            String.format(
                "     Y"
                    + (str + 1)
                    + " = (%9.6f)*X"
                    + origString
                    + " + (%9.6f)*Y"
                    + origString
                    + " + (%9.6f)*Z"
                    + origString
                    + " + (%12.6f)",
                btransforms.get(str).getElement(1, 0),
                btransforms.get(str).getElement(1, 1),
                btransforms.get(str).getElement(1, 2),
                btransforms.get(str).getElement(1, 3)));
        txt.append("\n");
        txt.append(
            String.format(
                "     Z"
                    + (str + 1)
                    + " = (%9.6f)*X"
                    + origString
                    + " + (%9.6f)*Y"
                    + origString
                    + " + (%9.6f)*Z"
                    + origString
                    + " + (%12.6f)",
                btransforms.get(str).getElement(2, 0),
                btransforms.get(str).getElement(2, 1),
                btransforms.get(str).getElement(2, 2),
                btransforms.get(str).getElement(2, 3)));
        txt.append("\n\n");
      }
    }
    return txt.toString();
  }