/* $Revision$ $Author$ $Date$
 *
 * Copyright (C) 2007  Ola Spjuth <ola.spjuth@farmbio.uu.se>
 *
 * Contact: cdk-devel@lists.sourceforge.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 * All we ask is that proper credit is given for our work, which includes
 * - but is not limited to - adding the above copyright notice to the beginning
 * of your source code files, and to any copyright notice that you may distribute
 * with programs based on this work.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

package org.openscience.cdk.io.cml;

import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IChemFile;
import org.openscience.cdk.libio.md.ChargeGroup;
import org.openscience.cdk.libio.md.MDMolecule;
import org.openscience.cdk.libio.md.Residue;
import org.xml.sax.Attributes;

/**
 * 
 * Implements a Convention for parsing an MDMolecule from CML.
 * 
 * @cdk.module libiomd
 * @cdk.githash
 * 
 * @author Ola Spjuth <ola.spjuth@farmbio.uu.se>
 *
 */
public class MDMoleculeConvention extends CMLCoreModule {

	private Residue currentResidue;
	private ChargeGroup currentChargeGroup;
	
	public MDMoleculeConvention(IChemFile chemFile) {
		super(chemFile);
	}

	public MDMoleculeConvention(ICMLModule conv) {
		super(conv);
	}

	/**
	 * Add parsing of elements in mdmolecule:
	 * 
	 * mdmolecule
	 * 		chargeGroup
	 * 			id
	 * 			cgNumber
	 * 			atomArray
	 * 			switchingAtom
	 * 		residue
	 * 			id
	 * 			title
	 * 			resNumber
	 * 			atomArray
	 * 
	 * @cdk.todo The JavaDoc of this class needs to be converted into HTML
	 */
	public void startElement(CMLStack xpath, String uri, String local, String raw, Attributes atts) {
//		<molecule convention="md:mdMolecule"
//	          xmlns="http://www.xml-cml.org/schema"
//	          xmlns:md="http://www.bioclipse.org/mdmolecule">
//	  <atomArray>
//	    <atom id="a1" elementType="C"/>
//	    <atom id="a2" elementType="C"/>
//	  </atomArray>
//	  <molecule dictRef="md:chargeGroup" id="cg1">
//	    <scalar dictRef="md:cgNumber">5</scalar>
//	    <atomArray>
//	      <atom ref="a1"/>
//	      <atom ref="a2"><scalar dictRef="md:switchingAtom"/></atom>
//	    </atomArray>
//	  </molecule>
//	  <molecule dictRef="md:residue" id="r1" title="resName">
//	    <scalar dictRef="md:resNumber">3</scalar>
//	    <atomArray>
//	      <atom ref="a1"/>
//	      <atom ref="a2"/>
//	    </atomArray>
//	  </molecule>
//	</molecule>

		// let the CMLCore convention deal with things first

		if ("molecule".equals(local)) {

			// the copy the parsed content into a new MDMolecule
			if (atts.getValue("convention") != null &&
				atts.getValue("convention").equals("md:mdMolecule")) {
//				System.out.println("creating a MDMolecule");
				super.startElement(xpath, uri, local, raw, atts);
				currentMolecule = new MDMolecule(currentMolecule);
			} else {
				DICTREF = atts.getValue("dictRef") != null ? atts.getValue("dictRef") : "";
				//If residue or chargeGroup, set up a new one
				if (DICTREF.equals("md:chargeGroup")){
//					System.out.println("Creating a new charge group...");
					currentChargeGroup = new ChargeGroup();
				} else if (DICTREF.equals("md:residue")){
//					System.out.println("Creating a new residue group...");
					currentResidue = new Residue();
					if (atts.getValue("title")!=null)
						currentResidue.setName(atts.getValue("title"));
				}
			}
		} else 
		
		//We have a scalar element. Now check who it belongs to
		if ("scalar".equals(local)) {
			DICTREF = atts.getValue("dictRef");
			//Switching Atom
			if ("md:switchingAtom".equals(DICTREF)){
				//Set current atom as switching atom
				System.out.println("Adding Switching atom: " + currentAtom);
				currentChargeGroup.setSwitchingAtom(currentAtom);
			} else {
				super.startElement(xpath, uri, local, raw, atts);
			}
		}
		
		else if ("atom".equals(local)) {
			if (currentChargeGroup != null) {
				String id = atts.getValue("ref");
				if (id != null) {
					// ok, an atom is referenced; look it up
					currentAtom = null;
//					System.out.println("#atoms: " + currentMolecule.getAtomCount());
					for (IAtom nextAtom : currentMolecule.atoms()) {
						if (nextAtom.getID().equals(id)) {
							currentAtom = nextAtom; 
						}
					}
					if (currentAtom == null) {
						logger.error("Could not found the referenced atom '" + id + "' for this charge group!");
					} else {
						currentChargeGroup.addAtom(currentAtom);
					}
				}
			} else if (currentResidue != null) {
				String id = atts.getValue("ref");
				if (id != null) {
					// ok, an atom is referenced; look it up
					IAtom referencedAtom = null;
//					System.out.println("#atoms: " + currentMolecule.getAtomCount());
                    for (IAtom nextAtom : currentMolecule.atoms()) {
						if (nextAtom.getID().equals(id)) {
							referencedAtom = nextAtom; 
						}
					}
					if (referencedAtom == null) {
						logger.error("Could not found the referenced atom '" + id + "' for this residue!");
					} else {
						currentResidue.addAtom(referencedAtom);
					}
				}
			} else {
				// ok, fine, just add it to the currentMolecule
				super.startElement(xpath, uri, local, raw, atts);
			}
		}
		
		else {
			super.startElement(xpath, uri, local, raw, atts);
		}

	}

	/**
	 * Finish up parsing of elements in mdmolecule
	 */
	public void endElement(CMLStack xpath, String uri, String name, String raw) {
		if (name.equals("molecule")){
//			System.out.println("Ending element mdmolecule");
			// add chargeGroup, and then delete them
			if (currentChargeGroup != null) {
				if (currentMolecule instanceof MDMolecule) {
					((MDMolecule)currentMolecule).addChargeGroup(currentChargeGroup);
				} else {
					logger.error("Need to store a charge group, but the current molecule is not a MDMolecule!");
				}
				currentChargeGroup = null;
			} else 
			
			// add chargeGroup, and then delete them
			if (currentResidue != null) {
				if (currentMolecule instanceof MDMolecule) {
					((MDMolecule)currentMolecule).addResidue(currentResidue);
				} else {
					logger.error("Need to store a residue group, but the current molecule is not a MDMolecule!");
				}
				currentResidue = null;
			} else {
//				System.out.println("OK, that was the last end mdmolecule");
				super.endElement(xpath, uri, name, raw);
			}
		} else if ("atomArray".equals(name)) {
			if (xpath.length() == 2 && xpath.endsWith("molecule", "atomArray")) {
				storeAtomData();
			} else if (xpath.length() > 2 && xpath.endsWith("cml", "molecule", "atomArray")) {
				storeAtomData();
			}
		} else if ("bondArray".equals(name)) {
			if (xpath.length() == 2 && xpath.endsWith("molecule", "bondArray")) {
				storeBondData();
			} else if (xpath.length() > 2 && xpath.endsWith("cml", "molecule", "bondArray")) {
				storeBondData();
			}
		} else if ("scalar".equals(name)) {
			//Residue number
			if ("md:resNumber".equals(DICTREF)){
				int myInt=Integer.parseInt(currentChars);
				currentResidue.setNumber(myInt);
			}
			//ChargeGroup number
			else if ("md:cgNumber".equals(DICTREF)){
				int myInt=Integer.parseInt(currentChars);
				currentChargeGroup.setNumber(myInt);
			}
		} else {
			super.endElement(xpath, uri, name, raw);
		}
	}

}
