/* Copyright (C) 2004-2009  Ulrich Bauer <ulrich.bauer@alumni.tum.de>
 *
 * 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.ringsearch;

import org._3pq.jgrapht.UndirectedGraph;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.graph.MoleculeGraphs;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IRing;
import org.openscience.cdk.interfaces.IRingSet;
import org.openscience.cdk.ringsearch.cyclebasis.CycleBasis;
import org.openscience.cdk.ringsearch.cyclebasis.SimpleCycle;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * Finds the Smallest Set of Smallest Rings.
 * This is an implementation of an algorithm
 * by Franziska Berger, Peter Gritzmann, and Sven deVries, TU M&uuml;nchen,
 * {@cdk.cite BGdV04a}.
 *
 * <p>Additional related algorithms from {@cdk.cite BGdV04b}.
 *
 * @author Ulrich Bauer &lt;ulrich.bauer@alumni.tum.de&gt;
 *
 * @cdk.module standard
 * @cdk.githash
 *
 * @cdk.keyword smallest-set-of-rings
 * @cdk.keyword ring search
 * @cdk.dictref blue-obelisk:findSmallestSetOfSmallestRings_Berger
 * @deprecated Use the {@link org.openscience.cdk.graph.Cycles} API
 *             {@link org.openscience.cdk.graph.Cycles#sssr(IAtomContainer)}
 */
@Deprecated
public class SSSRFinder {

    private IAtomContainer atomContainer;
    private CycleBasis     cycleBasis;

    /**
     * Constructs a SSSRFinder for a specified molecule.
     *
     * @param   container the molecule to be searched for rings
     */
    public SSSRFinder(IAtomContainer container) {
        this.atomContainer = container;
    }

    /**
     * Finds a Smallest Set of Smallest Rings.
     * The returned set is not uniquely defined.
     *
     * @return      a RingSet containing the SSSR
     */
    public IRingSet findSSSR() {
        if (atomContainer == null) {
            return null;
        }
        IRingSet ringSet = toRingSet(atomContainer, cycleBasis().cycles());
        //		atomContainer.setProperty(CDKConstants.SMALLEST_RINGS, ringSet);
        return ringSet;

    }

    /**
     * Finds the Set of Essential Rings.
     * These rings are contained in every possible SSSR.
     * The returned set is uniquely defined.
     *
     * @return      a RingSet containing the Essential Rings
     */
    public IRingSet findEssentialRings() {
        if (atomContainer == null) {
            return null;
        }
        IRingSet ringSet = toRingSet(atomContainer, cycleBasis().essentialCycles());
        //		atomContainer.setProperty(CDKConstants.ESSENTIAL_RINGS, ringSet);
        return ringSet;
    }

    /**
     * Finds the Set of Relevant Rings.
     * These rings are contained in every possible SSSR.
     * The returned set is uniquely defined.
     *
     * @return      a RingSet containing the Relevant Rings
     */
    public IRingSet findRelevantRings() {
        if (atomContainer == null) {
            return null;
        }

        IRingSet ringSet = toRingSet(atomContainer, cycleBasis().relevantCycles().keySet());
        //		atomContainer.setProperty(CDKConstants.RELEVANT_RINGS, ringSet);
        return ringSet;
    }

    /**
     * Finds the "interchangeability" equivalence classes.
     * The interchangeability relation is described in [GLS00].
     *
     * @return      a List of RingSets containing the rings in an equivalence class
     */
    public List findEquivalenceClasses() {
        if (atomContainer == null) {
            return null;
        }

        List<IRingSet> equivalenceClasses = new ArrayList<IRingSet>();
        for (Object o : cycleBasis().equivalenceClasses()) {
            equivalenceClasses.add(toRingSet(atomContainer, (Collection) o));
        }
        return equivalenceClasses;
    }

    /**
     * Returns a vector containing the lengths of the rings in a SSSR.
     * The vector is uniquely defined for any SSSR of a molecule.
     *
     * @return An <code>int[]</code> containing the length of the rings in a SSSR
     */
    public int[] getSSSRWeightVector() {
        return cycleBasis().weightVector();
    }

    /**
     * Returns a vector containing the size of the "interchangeability" equivalence classes.
     * The vector is uniquely defined for any SSSR of a molecule.
     *
     * @return An <code>int[]</code> containing the size of the equivalence classes in a SSSR
     */
    public int[] getEquivalenceClassesSizeVector() {
        List equivalenceClasses = cycleBasis().equivalenceClasses();
        int[] result = new int[equivalenceClasses.size()];
        for (int i = 0; i < equivalenceClasses.size(); i++) {
            result[i] = ((Collection) equivalenceClasses.get(i)).size();
        }
        return result;
    }

    private CycleBasis cycleBasis() {
        if (cycleBasis == null) {
            UndirectedGraph molGraph = MoleculeGraphs.getMoleculeGraph(atomContainer);

            cycleBasis = new CycleBasis(molGraph);
        }
        return cycleBasis;
    }

    private static IRingSet toRingSet(IAtomContainer container, Collection cycles) {

        IRingSet ringSet = container.getBuilder().newInstance(IRingSet.class);

        Iterator cycleIterator = cycles.iterator();

        while (cycleIterator.hasNext()) {
            SimpleCycle cycle = (SimpleCycle) cycleIterator.next();

            IRing ring = container.getBuilder().newInstance(IRing.class);

            List vertices = cycle.vertexList();

            IAtom[] atoms = new IAtom[vertices.size()];
            atoms[0] = (IAtom) vertices.get(0);
            for (int i = 1; i < vertices.size(); i++) {
                atoms[i] = (IAtom) vertices.get(i);
                ring.addElectronContainer(container.getBond(atoms[i - 1], atoms[i]));
            }

            for (IAtom atom : atoms)
                atom.setFlag(CDKConstants.ISINRING, true);

            ring.addElectronContainer(container.getBond(atoms[vertices.size() - 1], atoms[0]));
            ring.setAtoms(atoms);

            ringSet.addAtomContainer(ring);
        }

        return ringSet;

    }

}
