/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 */

package org.biojava.nbio.structure;

import org.biojava.nbio.structure.io.PDBParseException;

import java.util.ArrayList;
import java.util.List;


/** A class that can change one amino acid to another. Side chain atoms are neglected, only the Cb atom is kept.
 *
 *
 * example usage:
 * <pre>
String outputfile =  "/Users/ap3/WORK/PDB/mutated.pdb" ;

Structure struc = StructureIO.getStructure("5pti");
System.out.println(struc);

String chainId = "A";
String pdbResnum = "3";
String newType = "ARG";

// mutate the original structure and create a new one.
Mutator m = new Mutator();
Structure newstruc = m.mutate(struc,chainId,pdbResnum,newType);

FileOutputStream out= new FileOutputStream(outputfile);
PrintStream p =  new PrintStream( out );

p.println (newstruc.toPDB());

p.close();
</pre>
 * @author Andreas Prlic
 * @since 1.5
 * @version %I% %G%
 */
public class Mutator{
	List<String> supportedAtoms;

	public Mutator(){
		supportedAtoms = new ArrayList<String>();
		supportedAtoms.add("N");
		supportedAtoms.add("CA");
		supportedAtoms.add("C");
		supportedAtoms.add("O");
		supportedAtoms.add("CB");
	}

	/** creates a new structure which is identical with the original one.
	 * only one amino acid will be different.
	 *
	 *
	 *
	 *
	 * @param struc the structure object that is the container for the residue to be mutated
	 * @param chainId the id (name) of the chain to be mutated. @see Chain.getName()
	 * @param pdbResnum the PDB residue number of the residue
	 * @param newType the new residue type (3 characters)
	 * @return a structure object where one residue has been modified
	 * @throws PDBParseException
	 */
	public Structure  mutate(Structure struc, String chainId, String pdbResnum, String newType)
	throws PDBParseException{


		// create a  container for the new structure
		Structure newstruc = new StructureImpl();

		// first we need to find our corresponding chain

		// get the chains for model nr. 0
		// if structure is xray there will be only one "model".
		List<Chain> chains = struc.getChains(0);

		// iterate over all chains.
		for (Chain c : chains) {
			if (c.getChainID().equals(chainId)) {
				// here is our chain!

				Chain newchain = new ChainImpl();
				newchain.setChainID(c.getChainID());

				List<Group> groups = c.getAtomGroups();

				// now iterate over all groups in this chain.
				// in order to find the amino acid that has this pdbRenum.

				for (Group g : groups) {
					String rnum = g.getResidueNumber().toString();

					// we only mutate amino acids
					// and ignore hetatoms and nucleotides in this case
					if (rnum.equals(pdbResnum) && (g.getType() == GroupType.AMINOACID)) {

						// create the mutated amino acid and add it to our new chain
						AminoAcid newgroup = mutateResidue((AminoAcid) g, newType);
						newchain.addGroup(newgroup);
					} else {
						// add the group  to the new chain unmodified.
						newchain.addGroup(g);
					}
				}

				// add the newly constructed chain to the structure;
				newstruc.addChain(newchain);
			} else {
				// this chain is not requested, add it to the new structure unmodified.
				newstruc.addChain(c);
			}

		}
		return newstruc;
	}

	/** create a new residue which is of the new type.
	 * Only the atoms N, Ca, C, O, Cb will be considered.
	 * @param oldAmino
	 * @param newType
	 * @return a new, mutated, residue
	 * @throws PDBParseException
	 */
	public AminoAcid mutateResidue(AminoAcid oldAmino, String newType)
	throws PDBParseException {

		AminoAcid newgroup = new AminoAcidImpl();

		newgroup.setResidueNumber(oldAmino.getResidueNumber());
		newgroup.setPDBName(newType);


		AtomIterator aiter =new AtomIterator(oldAmino);
		while (aiter.hasNext()){
			Atom a = aiter.next();
			if ( supportedAtoms.contains(a.getName())){
				newgroup.addAtom(a);
			}
		}

		return newgroup;

	}

}
