/* $Revision$ $Author$ $Date$    
 * 
 * Copyright (C) 1997-2007  The Chemistry Development Kit (CDK) project
 * 
 * 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.
 * 
 * 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.graph;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import org.openscience.cdk.interfaces.*;

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

/**
 * Tool class for checking whether the (sub)structure in an
 * AtomContainer is connected.
 * To check whether an AtomContainer is connected this code
 * can be used:
 * <pre>
 *  boolean isConnected = ConnectivityChecker.isConnected(atomContainer);
 * </pre>
 *
 * <p>A disconnected AtomContainer can be fragmented into connected
 * fragments by using code like:
 * <pre>
 *   MoleculeSet fragments = ConnectivityChecker.partitionIntoMolecules(disconnectedContainer);
 *   int fragmentCount = fragments.getMoleculeCount();
 * </pre> 
 *
 * @cdk.module standard
 * @cdk.githash
 *
 * @cdk.keyword connectivity
 */
@TestClass("org.openscience.cdk.graph.ConnectivityCheckerTest")
public class ConnectivityChecker 
{
	/**
	 * Check whether a set of atoms in an {@link IAtomContainer} is connected.
	 *
	 * @param   atomContainer  The {@link IAtomContainer} to be check for connectedness
	 * @return                 true if the {@link IAtomContainer} is connected   
	 */
    @TestMethod("testIsConnected_IAtomContainer,testPartitionIntoMolecules_IsConnected_Consistency")
    public static boolean isConnected(IAtomContainer atomContainer)
	{
        // with one atom or less, we define it to be connected, as there is no
        // partitioning needed
        if (atomContainer.getAtomCount() < 2) return true;

		IAtomContainer newContainer = atomContainer.getBuilder().newAtomContainer();
		IMolecule molecule = atomContainer.getBuilder().newMolecule();
		List<IAtom> sphere = new ArrayList<IAtom>();
        for (IAtom atom : atomContainer.atoms()) {
            atom.setFlag(CDKConstants.VISITED, false);
            newContainer.addAtom(atom);
        }

        for (IBond bond : atomContainer.bonds()) {
			bond.setFlag(CDKConstants.VISITED, false);
			newContainer.addBond(bond);
		}
        
        IAtom atom = newContainer.getAtom(0);
		sphere.add(atom);
		atom.setFlag(CDKConstants.VISITED, true);
		PathTools.breadthFirstSearch(newContainer, sphere, molecule);
        return molecule.getAtomCount() == atomContainer.getAtomCount();
    }
	


	/**
	 * Partitions the atoms in an AtomContainer into covalently connected components.
	 *
	 * @param   atomContainer  The AtomContainer to be partitioned into connected components, i.e. molecules
	 * @return                 A MoleculeSet.
     *
     * @cdk.dictref   blue-obelisk:graphPartitioning
	 */
    @TestMethod("testPartitionIntoMolecules_IAtomContainer,testPartitionIntoMoleculesKeepsAtomIDs,testPartitionIntoMolecules_IsConnected_Consistency")
    public static IMoleculeSet partitionIntoMolecules(IAtomContainer atomContainer) {
		IAtomContainer newContainer = atomContainer.getBuilder().newAtomContainer();
		IMolecule molecule;
		IMoleculeSet molecules = atomContainer.getBuilder().newMoleculeSet();
		List<IAtom> sphere = new ArrayList<IAtom>();

        for (IAtom atom : atomContainer.atoms()) {
            atom.setFlag(CDKConstants.VISITED, false);
            newContainer.addAtom(atom);
        }

        for (IBond bond : atomContainer.bonds()) {
            bond.setFlag(CDKConstants.VISITED, false);
            newContainer.addBond(bond);
        }

        for (IElectronContainer eContainer : atomContainer.electronContainers()) {
            eContainer.setFlag(CDKConstants.VISITED, false);
            newContainer.addElectronContainer(eContainer);
        }

        while(newContainer.getAtomCount() > 0) {
			IAtom atom = newContainer.getAtom(0);
			molecule = atomContainer.getBuilder().newMolecule();
			sphere.clear();
			sphere.add(atom);
			atom.setFlag(CDKConstants.VISITED, true);
			PathTools.breadthFirstSearch(newContainer, sphere, molecule);
			molecules.addMolecule(molecule);
			newContainer.remove(molecule);
		}
		return molecules;
	}
}
