/// <summary> Calculate the number of missing hydrogens by substracting the number of
        /// bonds for the atom from the expected number of bonds. Charges are included
        /// in the calculation. The number of expected bonds is defined by the AtomType
        /// generated with the AtomTypeFactory.
        ///
        /// </summary>
        /// <param name="atom">     Description of the Parameter
        /// </param>
        /// <param name="throwExceptionForUnknowAtom"> Should an exception be thrown if an unknown atomtype is found or 0 returned ?
        /// </param>
        /// <returns>           Description of the Return Value
        /// </returns>
        /// <seealso cref="AtomTypeFactory">
        /// </seealso>
        public virtual int calculateNumberOfImplicitHydrogens(IAtom atom, double bondOrderSum, IBond[] connectedBonds, bool throwExceptionForUnknowAtom)
        {
            int missingHydrogen = 0;

            if (atom is IPseudoAtom)
            {
                // don't figure it out... it simply does not lack H's
            }
            else if (atom.AtomicNumber == 1 || atom.Symbol.Equals("H"))
            {
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                missingHydrogen = (int)(1 - bondOrderSum - atom.getFormalCharge());
            }
            else
            {
                //logger.info("Calculating number of missing hydrogen atoms");
                // get default atom
                IAtomType[] atomTypes = getAtomTypeFactory(atom.Builder).getAtomTypes(atom.Symbol);
                if (atomTypes.Length == 0 && throwExceptionForUnknowAtom)
                {
                    return(0);
                }
                //logger.debug("Found atomtypes: " + atomTypes.Length);
                if (atomTypes.Length > 0)
                {
                    IAtomType defaultAtom = atomTypes[0];
                    //logger.debug("DefAtom: ", defaultAtom);
                    //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                    missingHydrogen = (int)(defaultAtom.BondOrderSum - bondOrderSum + atom.getFormalCharge());
                    if (atom.getFlag(CDKConstants.ISAROMATIC))
                    {
                        bool subtractOne = true;
                        for (int i = 0; i < connectedBonds.Length; i++)
                        {
                            if (connectedBonds[i].Order == 2 || connectedBonds[i].Order == CDKConstants.BONDORDER_AROMATIC)
                            {
                                subtractOne = false;
                            }
                        }
                        if (subtractOne)
                        {
                            missingHydrogen--;
                        }
                    }
                    //logger.debug("Atom: ", atom.Symbol);
                    //logger.debug("  max bond order: " + defaultAtom.BondOrderSum);
                    //logger.debug("  bond order sum: " + bondOrderSum);
                    //logger.debug("  charge        : " + atom.getFormalCharge());
                }
                else
                {
                    //logger.warn("Could not find atom type for ", atom.Symbol);
                }
            }
            return(missingHydrogen);
        }
        /// <summary>  Generates the SMILES string for the atom
        /// 
        /// </summary>
        /// <param name="a">                       the atom to generate the SMILES for.
        /// </param>
        /// <param name="buffer">                  the string buffer that the atom is to be
        /// apended to.
        /// </param>
        /// <param name="container">               the AtomContainer to analyze.
        /// </param>
        /// <param name="chiral">                  is a chiral smiles wished?
        /// </param>
        /// <param name="parent">                  the atom we came from.
        /// </param>
        /// <param name="atomsInOrderOfSmiles">    a vector containing the atoms in the order
        /// they are in the smiles.
        /// </param>
        /// <param name="currentChain">            The chain we currently deal with.
        /// </param>
        /// <param name="doubleBondConfiguration"> Description of Parameter
        /// </param>
        private void parseAtom(IAtom a, System.Text.StringBuilder buffer, IAtomContainer container, bool chiral, bool[] doubleBondConfiguration, IAtom parent, System.Collections.ArrayList atomsInOrderOfSmiles, System.Collections.ArrayList currentChain)
        {
            System.String symbol = a.Symbol;
            bool stereo = BondTools.isStereo(container, a);
            bool brackets = symbol.Equals("B") || symbol.Equals("C") || symbol.Equals("N") || symbol.Equals("O") || symbol.Equals("P") || symbol.Equals("S") || symbol.Equals("F") || symbol.Equals("Br") || symbol.Equals("I") || symbol.Equals("Cl");
            brackets = !brackets;
            //System.out.println("in parseAtom()");
            //Deal with the start of a double bond configuration
            if (isStartOfDoubleBond(container, a, parent, doubleBondConfiguration))
            {
                buffer.Append('/');
            }

            if (a is IPseudoAtom)
            {
                buffer.Append("[*]");
            }
            else
            {
                System.String mass = generateMassString(a);
                brackets = brackets | !mass.Equals("");

                System.String charge = generateChargeString(a);
                brackets = brackets | !charge.Equals("");

                if (chiral && stereo)
                {
                    brackets = true;
                }
                if (brackets)
                {
                    buffer.Append('[');
                }
                buffer.Append(mass);
                if (a.getFlag(CDKConstants.ISAROMATIC))
                {
                    // Strictly speaking, this is wrong. Lower case is only used for sp2 atoms!
                    buffer.Append(a.Symbol.ToLower());
                }
                else if (a.Hybridization == CDKConstants.HYBRIDIZATION_SP2)
                {
                    buffer.Append(a.Symbol.ToLower());
                }
                else
                {
                    buffer.Append(symbol);
                }
                if (a.getProperty(RING_CONFIG) != null && a.getProperty(RING_CONFIG).Equals(UP))
                {
                    buffer.Append('/');
                }
                if (a.getProperty(RING_CONFIG) != null && a.getProperty(RING_CONFIG).Equals(DOWN))
                {
                    buffer.Append('\\');
                }
                if (chiral && stereo && (BondTools.isTrigonalBipyramidalOrOctahedral(container, a) != 0 || BondTools.isSquarePlanar(container, a) || BondTools.isTetrahedral(container, a, false) != 0))
                {
                    buffer.Append('@');
                }
                if (chiral && stereo && BondTools.isSquarePlanar(container, a))
                {
                    buffer.Append("SP1");
                }
                //chiral
                //hcount
                buffer.Append(charge);
                if (brackets)
                {
                    buffer.Append(']');
                }
            }
            //System.out.println("in parseAtom() after dealing with Pseudoatom or not");
            //Deal with the end of a double bond configuration
            if (isEndOfDoubleBond(container, a, parent, doubleBondConfiguration))
            {
                IAtom viewFrom = null;
                for (int i = 0; i < currentChain.Count; i++)
                {
                    if (currentChain[i] == parent)
                    {
                        int k = i - 1;
                        while (k > -1)
                        {
                            if (currentChain[k] is IAtom)
                            {
                                viewFrom = (IAtom)currentChain[k];
                                break;
                            }
                            k--;
                        }
                    }
                }
                if (viewFrom == null)
                {
                    for (int i = 0; i < atomsInOrderOfSmiles.Count; i++)
                    {
                        if (atomsInOrderOfSmiles[i] == parent)
                        {
                            viewFrom = (IAtom)atomsInOrderOfSmiles[i - 1];
                        }
                    }
                }
                bool afterThisAtom = false;
                IAtom viewTo = null;
                for (int i = 0; i < currentChain.Count; i++)
                {
                    if (afterThisAtom && currentChain[i] is IAtom)
                    {
                        viewTo = (IAtom)currentChain[i];
                        break;
                    }
                    if (afterThisAtom && currentChain[i] is System.Collections.ArrayList)
                    {
                        viewTo = (IAtom)((System.Collections.ArrayList)currentChain[i])[0];
                        break;
                    }
                    if (a == currentChain[i])
                    {
                        afterThisAtom = true;
                    }
                }
                try
                {
                    if (BondTools.isCisTrans(viewFrom, a, parent, viewTo, container))
                    {
                        buffer.Append('\\');
                    }
                    else
                    {
                        buffer.Append('/');
                    }
                }
                catch (CDKException ex)
                {
                    //If the user wants a double bond configuration, where there is none, we ignore this.
                }
            }
            System.Collections.ArrayList v = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
            System.Collections.IEnumerator it = getRingOpenings(a, v).GetEnumerator();
            System.Collections.IEnumerator it2 = v.GetEnumerator();
            //System.out.println("in parseAtom() after checking for Ring openings");
            //UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
            while (it.MoveNext())
            {
                //UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
                System.Int32 integer = (System.Int32)it.Current;
                //UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
                IBond b = container.getBond((IAtom)it2.Current, a);
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                int type = (int)b.Order;
                if (type == 2 && !b.getFlag(CDKConstants.ISAROMATIC))
                {
                    buffer.Append("=");
                }
                else if (type == 3 && !b.getFlag(CDKConstants.ISAROMATIC))
                {
                    buffer.Append("#");
                }
                buffer.Append(integer);
            }
            atomsInOrderOfSmiles.Add(a);
            //System.out.println("End of parseAtom()");
        }
 /// <summary>  Append the symbol for the bond order between <code>a1</code> and <code>a2</code>
 /// to the <code>line</code>.
 /// 
 /// </summary>
 /// <param name="line">          the StringBuffer that the bond symbol is appended to.
 /// </param>
 /// <param name="a1">            Atom participating in the bond.
 /// </param>
 /// <param name="a2">            Atom participating in the bond.
 /// </param>
 /// <param name="atomContainer"> the AtomContainer that the SMILES string is generated
 /// for.
 /// </param>
 private void parseBond(System.Text.StringBuilder line, IAtom a1, IAtom a2, IAtomContainer atomContainer)
 {
     //System.out.println("in parseBond()");
     if (a1.getFlag(CDKConstants.ISAROMATIC) && a1.getFlag(CDKConstants.ISAROMATIC))
     {
         return;
     }
     if (atomContainer.getBond(a1, a2) == null)
     {
         return;
     }
     int type = 0;
     //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
     type = (int)atomContainer.getBond(a1, a2).Order;
     if (type == 1)
     {
     }
     else if (type == 2)
     {
         line.Append("=");
     }
     else if (type == 3)
     {
         line.Append("#");
     }
     else
     {
         // //System.out.println("Unknown bond type");
     }
 }
        /// <summary>  Tests the <code>ring</code> in the <code>molecule</code> for aromaticity. Uses the
        /// H&uuml;ckle rule (4n + 2) pie electrons. sp<sup>2</sup> hybridized C contibute 1 electron non
        /// sp<sup>2</sup> hybridized heteroatoms contribute 2 electrons (N and O should never be sp in
        /// or anything else in a ring and d electron elements get to complicated)
        /// sp<sup>2</sup> hybridized heteroatoms contribute 1 electron hybridization is worked out by
        /// counting the number of bonds with order 2. Therefore sp<sup>2</sup> hybridization is assumed
        /// if there is one bond of order 2. Otherwise sp<sup>3</sup> hybridization is assumed.
        ///
        /// </summary>
        /// <param name="ring">     the ring to test
        /// </param>
        /// <param name="atomContainer"> the AtomContainer the ring is in
        /// </param>
        /// <returns>           true if the ring is aromatic false otherwise.
        /// </returns>
        protected internal static bool isAromatic(IRing ring, IAtomContainer atomContainer)
        {
            IAtom[] ringAtoms = ring.Atoms;
            int     eCount    = 0;

            IBond[] conectedBonds;
            int     numDoubleBond = 0;
            bool    allConnectedBondsSingle;

            for (int i = 0; i < ringAtoms.Length; i++)
            {
                IAtom atom = ringAtoms[i];
                numDoubleBond           = 0;
                allConnectedBondsSingle = true;
                conectedBonds           = atomContainer.getConnectedBonds(atom);
                for (int j = 0; j < conectedBonds.Length; j++)
                {
                    IBond bond = conectedBonds[j];
                    if (bond.Order == 2 && ring.contains(bond))
                    {
                        numDoubleBond++;
                    }
                    // Count the Electron if bond order = 1.5
                    else if (bond.Order == 1.5 && ring.contains(bond))
                    {
                        numDoubleBond = 1;
                    }

                    if (bond.Order != 1)
                    {
                        allConnectedBondsSingle = false;
                    }
                }
                if (numDoubleBond == 1)
                {
                    //C or heteroatoms both contibute 1 electron in sp2 hybridized form
                    eCount++;
                }
                else if (!atom.Symbol.Equals("C"))
                {
                    //Heteroatom probably in sp3 hybrid therefore 2 electrons contributed.
                    eCount = eCount + 2;
                }
                else if (atom.getFlag(CDKConstants.ISAROMATIC))
                {
                    eCount++;
                }
                else if (allConnectedBondsSingle && atom.Symbol.Equals("C") && atom.getFormalCharge() == 1.0)
                {
                    // This is for tropylium and kinds.
                    // Dependence on hybridisation would be better:
                    // empty p-orbital is needed
                    continue;
                }
                else
                {
                    return(false);
                }
            }
            if (eCount - 2 != 0 && (eCount - 2) % 4 == 0)
            {
                return(true);
            }
            return(false);
        }
		/// <summary> Calculate the number of missing hydrogens by substracting the number of
		/// bonds for the atom from the expected number of bonds. Charges are included
		/// in the calculation. The number of expected bonds is defined by the AtomType
		/// generated with the AtomTypeFactory.
		/// 
		/// </summary>
		/// <param name="atom">     Description of the Parameter
		/// </param>
		/// <param name="throwExceptionForUnknowAtom"> Should an exception be thrown if an unknown atomtype is found or 0 returned ?
		/// </param>
		/// <returns>           Description of the Return Value
		/// </returns>
		/// <seealso cref="AtomTypeFactory">
		/// </seealso>
		public virtual int calculateNumberOfImplicitHydrogens(IAtom atom, double bondOrderSum, IBond[] connectedBonds, bool throwExceptionForUnknowAtom)
		{
			int missingHydrogen = 0;
			if (atom is IPseudoAtom)
			{
				// don't figure it out... it simply does not lack H's
			}
			else if (atom.AtomicNumber == 1 || atom.Symbol.Equals("H"))
			{
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				missingHydrogen = (int) (1 - bondOrderSum - atom.getFormalCharge());
			}
			else
			{
				//logger.info("Calculating number of missing hydrogen atoms");
				// get default atom
				IAtomType[] atomTypes = getAtomTypeFactory(atom.Builder).getAtomTypes(atom.Symbol);
				if (atomTypes.Length == 0 && throwExceptionForUnknowAtom)
					return 0;
				//logger.debug("Found atomtypes: " + atomTypes.Length);
				if (atomTypes.Length > 0)
				{
					IAtomType defaultAtom = atomTypes[0];
					//logger.debug("DefAtom: ", defaultAtom);
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					missingHydrogen = (int) (defaultAtom.BondOrderSum - bondOrderSum + atom.getFormalCharge());
					if (atom.getFlag(CDKConstants.ISAROMATIC))
					{
						bool subtractOne = true;
						for (int i = 0; i < connectedBonds.Length; i++)
						{
							if (connectedBonds[i].Order == 2 || connectedBonds[i].Order == CDKConstants.BONDORDER_AROMATIC)
								subtractOne = false;
						}
						if (subtractOne)
							missingHydrogen--;
					}
					//logger.debug("Atom: ", atom.Symbol);
					//logger.debug("  max bond order: " + defaultAtom.BondOrderSum);
					//logger.debug("  bond order sum: " + bondOrderSum);
					//logger.debug("  charge        : " + atom.getFormalCharge());
				}
				else
				{
					//logger.warn("Could not find atom type for ", atom.Symbol);
				}
			}
			return missingHydrogen;
		}
        /// <summary> The method is known to fail for certain compounds. For more information, see
        /// cdk.test.limitations package.
        ///
        /// </summary>
        public virtual void  saturate(IAtomContainer atomContainer)
        {
            /* newSaturate(atomContainer);
             * }
             * public void oldSaturate(AtomContainer atomContainer) throws CDKException { */
            IAtom partner = null;
            IAtom atom    = null;

            IAtom[]     partners   = null;
            IAtomType[] atomTypes1 = null;
            IAtomType[] atomTypes2 = null;
            IBond       bond       = null;

            for (int i = 1; i < 4; i++)
            {
                // handle atoms with degree 1 first and then proceed to higher order
                for (int f = 0; f < atomContainer.AtomCount; f++)
                {
                    atom = atomContainer.getAtomAt(f);
                    //logger.debug("symbol: ", atom.Symbol);
                    atomTypes1 = getAtomTypeFactory(atom.Builder).getAtomTypes(atom.Symbol);
                    if (atomTypes1.Length > 0)
                    {
                        //logger.debug("first atom type: ", atomTypes1[0]);
                        if (atomContainer.getBondCount(atom) == i)
                        {
                            if (atom.getFlag(CDKConstants.ISAROMATIC) && atomContainer.getBondOrderSum(atom) < atomTypes1[0].BondOrderSum - atom.getHydrogenCount())
                            {
                                partners = atomContainer.getConnectedAtoms(atom);
                                for (int g = 0; g < partners.Length; g++)
                                {
                                    partner = partners[g];
                                    //logger.debug("Atom has " + partners.Length + " partners");
                                    atomTypes2 = getAtomTypeFactory(atom.Builder).getAtomTypes(partner.Symbol);
                                    if (atomTypes2.Length == 0)
                                    {
                                        return;
                                    }
                                    if (atomContainer.getBond(partner, atom).getFlag(CDKConstants.ISAROMATIC) && atomContainer.getBondOrderSum(partner) < atomTypes2[0].BondOrderSum - partner.getHydrogenCount())
                                    {
                                        //logger.debug("Partner has " + atomContainer.getBondOrderSum(partner) + ", may have: " + atomTypes2[0].BondOrderSum);
                                        bond = atomContainer.getBond(atom, partner);
                                        //logger.debug("Bond order was " + bond.Order);
                                        bond.Order = bond.Order + 1;
                                        //logger.debug("Bond order now " + bond.Order);
                                        break;
                                    }
                                }
                            }
                            if (atomContainer.getBondOrderSum(atom) < atomTypes1[0].BondOrderSum - atom.getHydrogenCount())
                            {
                                //logger.debug("Atom has " + atomContainer.getBondOrderSum(atom) + ", may have: " + atomTypes1[0].BondOrderSum);
                                partners = atomContainer.getConnectedAtoms(atom);
                                for (int g = 0; g < partners.Length; g++)
                                {
                                    partner = partners[g];
                                    //logger.debug("Atom has " + partners.Length + " partners");
                                    atomTypes2 = getAtomTypeFactory(atom.Builder).getAtomTypes(partner.Symbol);
                                    if (atomTypes2.Length == 0)
                                    {
                                        return;
                                    }
                                    if (atomContainer.getBondOrderSum(partner) < atomTypes2[0].BondOrderSum - partner.getHydrogenCount())
                                    {
                                        //logger.debug("Partner has " + atomContainer.getBondOrderSum(partner) + ", may have: " + atomTypes2[0].BondOrderSum);
                                        bond = atomContainer.getBond(atom, partner);
                                        //logger.debug("Bond order was " + bond.Order);
                                        bond.Order = bond.Order + 1;
                                        //logger.debug("Bond order now " + bond.Order);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }