/**
   * Get the resonance structures from an IMolecule.
   *
   * @param molecule The IMolecule to analyze
   * @return The different resonance structures
   */
  @TestMethod("testGetStructures_IMolecule")
  public IMoleculeSet getStructures(IMolecule molecule) {
    int countStructure = 0;
    IMoleculeSet setOfMol = molecule.getBuilder().newMoleculeSet();
    setOfMol.addMolecule(molecule);

    for (int i = 0; i < setOfMol.getMoleculeCount(); i++) {
      IMolecule mol = setOfMol.getMolecule(i);
      for (IReactionProcess aReactionsList : reactionsList) {
        IReactionProcess reaction = aReactionsList;
        IMoleculeSet setOfReactants = molecule.getBuilder().newMoleculeSet();
        setOfReactants.addMolecule(mol);
        try {
          IReactionSet setOfReactions = reaction.initiate(setOfReactants, null);
          if (setOfReactions.getReactionCount() != 0)
            for (int k = 0; k < setOfReactions.getReactionCount(); k++)
              for (int j = 0;
                  j < setOfReactions.getReaction(k).getProducts().getAtomContainerCount();
                  j++) {
                IMolecule product = setOfReactions.getReaction(k).getProducts().getMolecule(j);
                if (!existAC(setOfMol, product)) {
                  setOfMol.addMolecule(product);
                  countStructure++;
                  if (countStructure > maxStructures) return setOfMol;
                }
              }
        } catch (CDKException e) {
          e.printStackTrace();
        }
      }
    }
    return setOfMol;
  }
  /**
   * Test for SF bug #1309731.
   *
   * @cdk.bug 1309731
   */
  @Test
  public void testModelBuilder3D_keepChemObjectIDs() throws Exception {
    ModelBuilder3D mb3d = ModelBuilder3D.getInstance();

    IMolecule methanol = new org.openscience.cdk.Molecule();
    IChemObjectBuilder builder = methanol.getBuilder();

    IAtom carbon1 = builder.newInstance(IAtom.class, "C");
    carbon1.setID("carbon1");
    methanol.addAtom(carbon1);
    for (int i = 0; i < 3; i++) {
      IAtom hydrogen = builder.newInstance(IAtom.class, "H");
      methanol.addAtom(hydrogen);
      methanol.addBond(builder.newInstance(IBond.class, carbon1, hydrogen, IBond.Order.SINGLE));
    }
    IAtom oxygen1 = builder.newInstance(IAtom.class, "O");
    oxygen1.setID("oxygen1");
    methanol.addAtom(oxygen1);
    methanol.addBond(builder.newInstance(IBond.class, carbon1, oxygen1, IBond.Order.SINGLE));
    IAtom hydrogen = builder.newInstance(IAtom.class, "H");
    methanol.addAtom(hydrogen);
    methanol.addBond(builder.newInstance(IBond.class, hydrogen, oxygen1, IBond.Order.SINGLE));

    Assert.assertEquals(6, methanol.getAtomCount());
    Assert.assertEquals(5, methanol.getBondCount());

    mb3d.generate3DCoordinates(methanol, false);

    checkAverageBondLength(methanol);
    Assert.assertEquals("carbon1", carbon1.getID());
    Assert.assertEquals("oxygen1", oxygen1.getID());
  }
  /**
   * Initiate process. It is needed to call the addExplicitHydrogensToSatisfyValency from the class
   * tools.HydrogenAdder.
   *
   * @param reactants reactants of the reaction
   * @param agents agents of the reaction (Must be in this case null)
   * @exception CDKException Description of the Exception
   */
  @TestMethod("testInitiate_IMoleculeSet_IMoleculeSet")
  public IReactionSet initiate(IMoleculeSet reactants, IMoleculeSet agents) throws CDKException {

    logger.debug("initiate reaction: HeterolyticCleavagePBReaction");

    if (reactants.getMoleculeCount() != 1) {
      throw new CDKException("HeterolyticCleavagePBReaction only expects one reactant");
    }
    if (agents != null) {
      throw new CDKException("HeterolyticCleavagePBReaction don't expects agents");
    }

    IReactionSet setOfReactions =
        DefaultChemObjectBuilder.getInstance().newInstance(IReactionSet.class);
    IMolecule reactant = reactants.getMolecule(0);

    /* if the parameter hasActiveCenter is not fixed yet, set the active centers*/
    IParameterReact ipr = super.getParameterClass(SetReactionCenter.class);
    if (ipr != null && !ipr.isSetParameter()) setActiveCenters(reactant);

    Iterator<IBond> bondis = reactant.bonds().iterator();
    while (bondis.hasNext()) {
      IBond bondi = bondis.next();
      IAtom atom1 = bondi.getAtom(0);
      IAtom atom2 = bondi.getAtom(1);
      if (bondi.getFlag(CDKConstants.REACTIVE_CENTER)
          && bondi.getOrder() != IBond.Order.SINGLE
          && atom1.getFlag(CDKConstants.REACTIVE_CENTER)
          && atom2.getFlag(CDKConstants.REACTIVE_CENTER)
          && (atom1.getFormalCharge() == CDKConstants.UNSET ? 0 : atom1.getFormalCharge()) == 0
          && (atom2.getFormalCharge() == CDKConstants.UNSET ? 0 : atom2.getFormalCharge()) == 0
          && reactant.getConnectedSingleElectronsCount(atom1) == 0
          && reactant.getConnectedSingleElectronsCount(atom2) == 0) {

        /**/
        for (int j = 0; j < 2; j++) {

          ArrayList<IAtom> atomList = new ArrayList<IAtom>();
          if (j == 0) {
            atomList.add(atom1);
            atomList.add(atom2);
          } else {
            atomList.add(atom2);
            atomList.add(atom1);
          }
          ArrayList<IBond> bondList = new ArrayList<IBond>();
          bondList.add(bondi);

          IMoleculeSet moleculeSet = reactant.getBuilder().newInstance(IMoleculeSet.class);
          moleculeSet.addMolecule(reactant);
          IReaction reaction = mechanism.initiate(moleculeSet, atomList, bondList);
          if (reaction == null) continue;
          else setOfReactions.addReaction(reaction);
        }
      }
    }
    return setOfReactions;
  }
  /**
   * Initiate process.
   *
   * @param reactants reactants of the reaction.
   * @param agents agents of the reaction (Must be in this case null).
   * @exception CDKException Description of the Exception
   */
  @TestMethod("testInitiate_IMoleculeSet_IMoleculeSet")
  public IReactionSet initiate(IMoleculeSet reactants, IMoleculeSet agents) throws CDKException {
    logger.debug("initiate reaction: RadicalChargeSiteInitiationReaction");

    if (reactants.getMoleculeCount() != 1) {
      throw new CDKException("RadicalChargeSiteInitiationReaction only expects one reactant");
    }
    if (agents != null) {
      throw new CDKException("RadicalChargeSiteInitiationReaction don't expects agents");
    }

    IReactionSet setOfReactions = DefaultChemObjectBuilder.getInstance().newReactionSet();
    IMolecule reactant = reactants.getMolecule(0);

    /* if the parameter hasActiveCenter is not fixed yet, set the active centers*/
    IParameterReact ipr = super.getParameterClass(SetReactionCenter.class);
    if (ipr != null && !ipr.isSetParameter()) setActiveCenters(reactant);

    Iterator<IAtom> atoms = reactants.getMolecule(0).atoms().iterator();
    while (atoms.hasNext()) {
      IAtom atomi = atoms.next();
      if (atomi.getFlag(CDKConstants.REACTIVE_CENTER)
          && reactant.getConnectedSingleElectronsCount(atomi) == 1
          && atomi.getFormalCharge() == 1) {

        Iterator<IBond> bondis = reactant.getConnectedBondsList(atomi).iterator();

        while (bondis.hasNext()) {
          IBond bondi = bondis.next();

          if (bondi.getFlag(CDKConstants.REACTIVE_CENTER)
              && bondi.getOrder() == IBond.Order.SINGLE) {

            IAtom atomj = bondi.getConnectedAtom(atomi);
            if (atomj.getFlag(CDKConstants.REACTIVE_CENTER) && atomj.getFormalCharge() == 0) {

              Iterator<IBond> bondjs = reactant.getConnectedBondsList(atomj).iterator();
              while (bondjs.hasNext()) {
                IBond bondj = bondjs.next();

                if (bondj.equals(bondi)) continue;

                if (bondj.getFlag(CDKConstants.REACTIVE_CENTER)
                    && bondj.getOrder() == IBond.Order.SINGLE) {

                  IAtom atomk = bondj.getConnectedAtom(atomj);
                  if (atomk.getFlag(CDKConstants.REACTIVE_CENTER)
                      && atomk.getSymbol().equals("C")
                      && atomk.getFormalCharge() == 0) {

                    ArrayList<IAtom> atomList = new ArrayList<IAtom>();
                    atomList.add(atomi);
                    atomList.add(atomj);
                    atomList.add(atomk);
                    ArrayList<IBond> bondList = new ArrayList<IBond>();
                    bondList.add(bondi);
                    bondList.add(bondj);

                    IMoleculeSet moleculeSet = reactant.getBuilder().newMoleculeSet();
                    moleculeSet.addMolecule(reactant);
                    IReaction reaction = mechanism.initiate(moleculeSet, atomList, bondList);
                    if (reaction == null) continue;
                    else setOfReactions.addReaction(reaction);
                  }
                }
              }
            }
          }
        }
      }
    }
    return setOfReactions;
  }
  /**
   * Get the container which is found resonance from a IMolecule. It is based on looking if the
   * order of the bond changes.
   *
   * @param molecule The IMolecule to analyze
   * @return The different containers
   */
  @TestMethod("testGetContainers_IMolecule")
  public IAtomContainerSet getContainers(IMolecule molecule) {
    IAtomContainerSet setOfCont = molecule.getBuilder().newAtomContainerSet();
    IMoleculeSet setOfMol = getStructures(molecule);

    if (setOfMol.getMoleculeCount() == 0) return setOfCont;

    /*extraction of all bonds which has been produced a changes of order*/
    List<IBond> bondList = new ArrayList<IBond>();
    for (int i = 1; i < setOfMol.getMoleculeCount(); i++) {
      IMolecule mol = setOfMol.getMolecule(i);
      for (int j = 0; j < mol.getBondCount(); j++) {
        IBond bond = molecule.getBond(j);
        if (!mol.getBond(j).getOrder().equals(bond.getOrder())) {
          if (!bondList.contains(bond)) bondList.add(bond);
        }
      }
    }

    if (bondList.size() == 0) return null;

    int[] flagBelonging = new int[bondList.size()];
    for (int i = 0; i < flagBelonging.length; i++) flagBelonging[i] = 0;
    int[] position = new int[bondList.size()];
    int maxGroup = 1;

    /*Analysis if the bond are linked together*/
    List<IBond> newBondList = new ArrayList<IBond>();
    newBondList.add(bondList.get(0));

    int pos = 0;
    for (int i = 0; i < newBondList.size(); i++) {

      if (i == 0) flagBelonging[i] = maxGroup;
      else {
        if (flagBelonging[position[i]] == 0) {
          maxGroup++;
          flagBelonging[position[i]] = maxGroup;
        }
      }

      IBond bondA = newBondList.get(i);
      for (int ato = 0; ato < 2; ato++) {
        IAtom atomA1 = bondA.getAtom(ato);
        List<IBond> bondA1s = molecule.getConnectedBondsList(atomA1);
        for (int j = 0; j < bondA1s.size(); j++) {
          IBond bondB = bondA1s.get(j);
          if (!newBondList.contains(bondB))
            for (int k = 0; k < bondList.size(); k++)
              if (bondList.get(k).equals(bondB))
                if (flagBelonging[k] == 0) {
                  flagBelonging[k] = maxGroup;
                  pos++;
                  newBondList.add(bondB);
                  position[pos] = k;
                }
        }
      }
      // if it is final size and not all are added
      if (newBondList.size() - 1 == i)
        for (int k = 0; k < bondList.size(); k++)
          if (!newBondList.contains(bondList.get(k))) {
            newBondList.add(bondList.get(k));
            position[i + 1] = k;
            break;
          }
    }
    /*creating containers according groups*/
    for (int i = 0; i < maxGroup; i++) {
      IAtomContainer container = molecule.getBuilder().newAtomContainer();
      for (int j = 0; j < bondList.size(); j++) {
        if (flagBelonging[j] != i + 1) continue;
        IBond bond = bondList.get(j);
        IAtom atomA1 = bond.getAtom(0);
        IAtom atomA2 = bond.getAtom(1);
        if (!container.contains(atomA1)) container.addAtom(atomA1);
        if (!container.contains(atomA2)) container.addAtom(atomA2);
        container.addBond(bond);
      }
      setOfCont.addAtomContainer(container);
    }
    return setOfCont;
  }