/* $Revision$ $Author$ $Date$    
 * 
 * Copyright (C) 2000-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.structgen.stochastic.operator;

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

import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.math.RandomNumbersTool;
import org.openscience.cdk.structgen.stochastic.PartialFilledStructureMerger;

/**
 * Modified molecular structures by applying crossover operator on a pair of parent structures 
 * and generate a pair of offspring structures. Each of the two offspring structures inherits 
 * a certain fragments from both of its parents.
 * 
 * @cdk.module structgen
 * @cdk.githash
 */
public class CrossoverMachine  
{
	private IAtomContainer[] redChild,blueChild;
	private List redAtoms,blueAtoms;
	List children;	
	
	PartialFilledStructureMerger pfsm;
	
	/** selects a partitioning mode*/
	int splitMode = 2;	
	/** selects a partitioning scale*/	
	int numatoms = 5;
	/** Indicates that <code>crossover</code> is using SPLIT_MODE_RADNDOM mode. */
    public static final int SPLIT_MODE_RADNDOM = 0;
    /** Indicates that <code>crossover</code> is using SPLIT_MODE_DEPTH_FIRST mode. */
    public static final int SPLIT_MODE_DEPTH_FIRST = 1;	
    /** Indicates that <code>crossover</code> is using SPLIT_MODE_BREADTH_FIRST mode. */
    public static final int SPLIT_MODE_BREADTH_FIRST = 2;
	
    /**
     * Constructs a new CrossoverMachine operator.
     */
    public CrossoverMachine()
    {
	    redChild = new IAtomContainer[2];
		blueChild = new IAtomContainer[2];
		
		redAtoms = new ArrayList();
		blueAtoms = new ArrayList();		
		children = new ArrayList(2);
		try
		{
			pfsm = new PartialFilledStructureMerger();
		}
		catch(java.lang.Exception ex){ }		
    }
	
	/**
     * Performs the n point crossover of two <code>MolChromosome</code>
     * supplied by the <code>CrossInfo.parents</code> class and stores the resulting
     * chromosomes in <code>CrossInfo.children</code>.
     * 
     * @return an object storing information generated by the operator.
     * @exception IllegalArgumentException if some of the crosspoints defined are
     * greater than the size of the corresponding chromosome.
     */
    public List doCrossover(IAtomContainer dad, IAtomContainer mom) throws CDKException
    {
		int dim = dad.getAtomCount();
		
		/***randomly divide atoms into two parts: redAtoms and blueAtoms.***/
		redAtoms.clear();
		blueAtoms.clear();		
		
	    if (splitMode==SPLIT_MODE_RADNDOM)
		{
			/*better way to randomly divide atoms into two parts: redAtoms and blueAtoms.*/
			for (int i = 0; i < dim; i++)
				redAtoms.add(Integer.valueOf(i));
			for (int i = 0; i < (dim - numatoms); i++)
			{   int ranInt = RandomNumbersTool.randomInt(0,redAtoms.size()-1);
				redAtoms.remove(ranInt);
				blueAtoms.add(Integer.valueOf(ranInt));
			}
				 
		}
		else
		{
			/*split graph using depth/breadth first traverse*/
			ChemGraph graph = new ChemGraph(dad);
			graph.setNumAtoms(numatoms);
			if (splitMode==SPLIT_MODE_DEPTH_FIRST)
			{
				redAtoms = graph.pickDFgraph();
			}
			else
				redAtoms = graph.pickBFgraph();
				
			for (int i = 0; i < dim; i++){
				Integer element = Integer.valueOf(i);
				if (!(redAtoms.contains(element)))
				{
					blueAtoms.add(element);
				}	
			}
		}		
		/*** dividing over ***/
		
		
		redChild[0] = dad.getBuilder().newAtomContainer(dad); 
		blueChild[0] = dad.getBuilder().newAtomContainer(dad); 
		redChild[1] = dad.getBuilder().newAtomContainer(mom); 
		blueChild[1] = dad.getBuilder().newAtomContainer(mom); 
		
		
		for (int j = 0; j < blueAtoms.size(); j++)
		{
			Iterator bonds = redChild[1].bonds().iterator();
			while (bonds.hasNext()) {
				IBond bond = (IBond)bonds.next();
				if (bond.contains(redChild[0].getAtom(((Integer)blueAtoms.get(j)).intValue())))
				{
					redChild[0].removeBond(bond);
				}
			}
		}


		for (int j = 0; j < blueAtoms.size(); j++)
		{
			Iterator bonds = redChild[1].bonds().iterator();
			while (bonds.hasNext()) {
				IBond bond = (IBond)bonds.next();
				if (bond.contains(redChild[1].getAtom(((Integer)blueAtoms.get(j)).intValue())))
				{
					bonds.remove(); // this removes it from redChild[1] too
				}
			}
		}


		for (int j = 0; j < redAtoms.size(); j++)
		{
			Iterator bonds = blueChild[0].bonds().iterator();
			while (bonds.hasNext()) {
				IBond bond = (IBond)bonds.next();
				if (bond.contains(blueChild[0].getAtom(((Integer)redAtoms.get(j)).intValue())))
				{
					bonds.remove();
				}
			}
		}


		for (int j = 0; j < redAtoms.size(); j++) {
			Iterator bonds = blueChild[1].bonds().iterator();
			while (bonds.hasNext()) {
				IBond bond = (IBond)bonds.next();
				if (bond.contains(blueChild[1].getAtom(((Integer)redAtoms.get(j)).intValue())))
				{
					bonds.remove();
				}
			}
		}

		
		redChild[0].add(blueChild[1]);
		if (children.size()==2)
		{
			redChild[1].add(blueChild[0]);
		}
		
		
		
		for (int f = 0; f < children.size(); f++)
		{
			pfsm.setAtomContainer(redChild[f]);
			children.add(f, pfsm.generate());
		}
        return children;			
    } 
}
