/*
 *                    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/
 *
 * created at Apr 26, 2008
 */
package org.biojava.bio.structure;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

import org.biojava.bio.structure.io.PDBFileParser;
import org.biojava.bio.structure.io.mmcif.MMcifParser;
import org.biojava.bio.structure.io.mmcif.SimpleMMcifConsumer;
import org.biojava.bio.structure.io.mmcif.SimpleMMcifParser;

import junit.framework.TestCase;

public class MMcifTest extends TestCase {

	public void testLoad(){


		// test a simple protein
		comparePDB2cif("5pti","A");

		// test a protein with modified residues
		comparePDB2cif("1a4w","L");
		comparePDB2cif("1a4w","H");
		comparePDB2cif("1a4w","I");

		// test a DNA binding protein
		comparePDB2cif("1j59","A");
		comparePDB2cif("1j59","B");
		comparePDB2cif("1j59","C");
		comparePDB2cif("1j59","D");
		comparePDB2cif("1j59","E");
		comparePDB2cif("1j59","F");

		// test a NMR protein
		comparePDB2cif("2kc9","A");

	}


	private void comparePDB2cif(String id, String chainId){
		String fileName = "/files/"+id+".cif";
		InputStream inStream = this.getClass().getResourceAsStream(fileName);
		assertNotNull(inStream);

		MMcifParser parser = new SimpleMMcifParser();

		SimpleMMcifConsumer consumer = new SimpleMMcifConsumer();

		parser.addMMcifConsumer(consumer);

		try {
			parser.parse(new BufferedReader(new InputStreamReader(inStream)));
		} catch (IOException e){
			fail(e.getMessage());
		}
		Structure cifStructure = consumer.getStructure();
		assertNotNull(cifStructure);


		// load the PDB file via the PDB parser
		Structure pdbStructure = null;
		InputStream pinStream = this.getClass().getResourceAsStream("/files/"+id+".pdb");
		assertNotNull(inStream);

		PDBFileParser pdbpars = new PDBFileParser();
		try {
			pdbStructure = pdbpars.parsePDBFile(pinStream) ;
		} catch (IOException e) {
			e.printStackTrace();
		}

		assertNotNull(pdbStructure);

		//System.out.println(pdbStructure.toPDB());
		//System.out.println(cifStructure.toPDB());

		// now compare the results
		try {

			// chech NMR data
			assertEquals("the isNMR flag is not the same!", pdbStructure.isNmr(), cifStructure.isNmr());

			if ( pdbStructure.isNmr()){
				assertEquals("the nr of NMR models is not the same!", pdbStructure.nrModels(), pdbStructure.nrModels());
				checkNMR(pdbStructure);
				checkNMR(cifStructure);
			}

			//System.out.println(pdbStructure);
			//System.out.println(cifStructure);

			// compare amino acids in chain 1:
			Chain a_pdb = pdbStructure.getChainByPDB(chainId);
			Chain a_cif = cifStructure.getChainByPDB(chainId);
			//System.out.println(a_pdb.getAtomGroups());
			//System.out.println(a_cif.getAtomGroups());

			//for (Group g: a_cif.getAtomGroups()){
			//	System.out.println(g);
			//}
			//System.out.println("--");
			String pdb_SEQseq = a_pdb.getSeqResSequence();
			String cif_SEQseq = a_cif.getSeqResSequence();
			assertEquals("the SEQRES sequences don;t match!", pdb_SEQseq,cif_SEQseq);

			// actually this check not necessarily works, since there can be waters in PDB that we don;t deal with yet in cif...
			//assertEquals("the nr of ATOM record groups is not the same!" , a_pdb.getAtomLength(),a_cif.getAtomLength());
			for (int i = 0 ; i < a_pdb.getAtomGroups(GroupType.AMINOACID).size(); i++){
				Group gp = a_pdb.getAtomGroups(GroupType.AMINOACID).get(i);
				Group gc = a_cif.getAtomGroups(GroupType.AMINOACID).get(i);
				checkGroups(gp,gc);
			}



			String pdb_seq = a_pdb.getAtomSequence();
			String cif_seq = a_cif.getAtomSequence();

			//System.out.println(pdb_seq);
			//System.out.println(cif_seq);

			assertEquals("the sequences obtained from PDB and mmCif don't match!",pdb_seq, cif_seq);

			List<DBRef> pdb_dbrefs= pdbStructure.getDBRefs();
			List<DBRef> cif_dbrefs= cifStructure.getDBRefs();

			assertEquals("nr of DBrefs found does not match!", pdb_dbrefs.size(),cif_dbrefs.size());

			DBRef p = pdb_dbrefs.get(0);
			DBRef c = cif_dbrefs.get(0);

			//System.out.println(p.toPDB());
			//System.out.println(c.toPDB());
			String pdb_dbref = p.toPDB();
			String cif_dbref = c.toPDB();
			assertEquals("DBRef is not equal",pdb_dbref,cif_dbref);

			PDBHeader h1 = pdbStructure.getPDBHeader();
			PDBHeader h2 = cifStructure.getPDBHeader();

			//compareString(h1.toPDB() ,h2.toPDB());
			//System.out.println(h1.toPDB());
			//System.out.println(h2.toPDB());
			assertEquals("the PDBHeader.toPDB representation is not equivalent", h1.toPDB().toUpperCase(),h2.toPDB().toUpperCase());

			// and the ultimate test!
			// but we are not there yet...
			// TODO: still need to parse SSBOND equivalent info from cif files...
			//assertEquals("the Structure.toPDB representation is not equivalent", pdbStructure.toPDB(),cifStructure.toPDB());

		} catch (StructureException ex){
			fail(ex.getMessage() + " for PDB: " + id);
		}

	}

	private void checkGroups(Group g1, Group g2){

	   //System.out.println("comparing " +g1 + " " + g2);

		assertEquals(g1.getType(),g2.getType());
		assertEquals(g1.getPDBCode(),g2.getPDBCode());
		assertEquals(g1.getPDBName(),g2.getPDBName());
		assertEquals(g1.has3D(),g2.has3D());
		assertEquals(g1.getAtoms().size(), g2.getAtoms().size());
		if ( g1.has3D()){
			try {
			Atom a1 = g1.getAtom(0);
			Atom a2 = g2.getAtom(0);
			assertEquals(a1.getX(),a2.getX());
			assertEquals(a1.getOccupancy(),a2.getOccupancy());
			assertEquals(a1.getTempFactor(),a2.getTempFactor());
			assertEquals(a1.getFullName(),a2.getFullName());
			} catch (StructureException e){
				fail(e.getMessage());
			}

		}


	}

	private void checkNMR(Structure s){
		assertTrue(s.isNmr());

		int models = s.nrModels();
		assertTrue(models > 0);

		List<Chain> model0 = s.getModel(0);

		// compare with all others
		for (int i = 1 ; i < models; i++){
			List<Chain> modelX = s.getModel(i);

			assertEquals(model0.size(),modelX.size());

			// compare lengths:
			for (int j=0 ; j< model0.size();j++){
				Chain c1 = model0.get(j);
				Chain cx = modelX.get(j);
				assertEquals(c1.getAtomLength(),cx.getAtomLength());
				// can;t compare seq res, since this is only done for 1st...
				//assertEquals("c1.getSeqResLength(),cx.getSeqResLength());
				assertEquals(c1.getAtomSequence(),cx.getAtomSequence());
				assertEquals(c1.getLengthAminos(),cx.getLengthAminos());
				assertEquals(c1.getAtomGroups(GroupType.AMINOACID).size(),cx.getAtomGroups(GroupType.AMINOACID).size());
				assertEquals(c1.getAtomGroups(GroupType.NUCLEOTIDE).size(),cx.getAtomGroups(GroupType.NUCLEOTIDE).size());
				assertEquals(c1.getAtomGroups(GroupType.HETATM).size(),cx.getAtomGroups(GroupType.HETATM).size());
			}


		}
	}

	private void compareString(String t, String pdb){
		for (int i =0 ; i < t.length() ; i++){
			System.out.println(">"+t.charAt(i)+":"+ pdb.charAt(i)+"<");
			if ( Character.toUpperCase(t.charAt(i)) != Character.toUpperCase(pdb.charAt(i))){

				break;
			}
		}
	}


}
