/* Copyright (C) 2004-2007  Christian Hoppe <chhoppe@users.sf.net>
 *
 *  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.modeling.builder3d;

import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.aromaticity.Aromaticity;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.exception.NoSuchAtomTypeException;
import org.openscience.cdk.graph.Cycles;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IAtomType;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemObjectBuilder;
import org.openscience.cdk.interfaces.IPseudoAtom;
import org.openscience.cdk.interfaces.IRing;
import org.openscience.cdk.interfaces.IRingSet;
import org.openscience.cdk.tools.HOSECodeGenerator;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
import org.openscience.cdk.tools.manipulator.RingSetManipulator;

/**
 *  Reads in a force field configuration file, set the atom types into a vector, and the data into a hashtable
 *  Therefore, it uses the class {@link MM2BasedParameterSetReader}.
 *  private Hashtable parameterSet;
 *  key=nameofdatafield+atomid1+;atomid2;atomxid
 *
 *  <p>MM2 and MMFF94 force field are implemented
 *  With force field data it configures the cdk atom (assign atomtype, van der Waals radius, charge...)
 *
 * @author     chhoppe
 * @cdk.created    2004-09-07
 * @cdk.module     forcefield
 * @cdk.githash
 */
public class ForceFieldConfigurator {

    private String                        ffName       = "mmff94";
    private List<IAtomType>               atomTypes;
    private Map<String, Object>           parameterSet = null;
    private MM2BasedParameterSetReader    mm2          = null;
    private MMFF94BasedParameterSetReader mmff94       = null;
    private InputStream                   ins          = null;
    private String[]                      fftypes      = {"mm2", "mmff94"};

    /**
     *Constructor for the ForceFieldConfigurator object
     */
    public ForceFieldConfigurator() {}

    /**
     *  Sets the inputStream attribute of the ForceFieldConfigurator object
     *
     * @param  ins  The new inputStream value
     */
    public void setInputStream(InputStream ins) {
        this.ins = ins;
    }

    /**
     *  gives a list of possible force field types
     *
     *@return                the list
     */
    public String[] getFfTypes() {
        return fftypes;
    }

    /**
     *  Sets the forceFieldType attribute of the ForceFieldConfigurator object
     *
     * @param  ffname  The new forceFieldType name
     */

    public boolean checkForceFieldType(String ffname) {
        boolean check = false;
        for (int i = 0; i < fftypes.length; i++) {
            if (fftypes[i].equals(ffname)) {
                check = true;
                break;
            }
        }
        if (!check) {
            //			logger.debug("FFError:checkForceFieldType> Unknown forcefield:" + ffname + "Take default:"+ffName);
            return false;
        }
        return true;
    }

    /**
     *Constructor for the ForceFieldConfigurator object
     *
     * @param  ffname  name of the force field data file
     */
    public void setForceFieldConfigurator(String ffname, IChemObjectBuilder builder) throws CDKException {
        ffname = ffname.toLowerCase();
        boolean check = false;

        if (ffname == ffName && parameterSet != null) {
        } else {
            check = this.checkForceFieldType(ffname);
            ffName = ffname;
            if (ffName.equals("mm2")) {
                //logger.debug("ForceFieldConfigurator: open Force Field mm2");
                //f = new File(mm2File);
                //readFile(f);
                ins = this.getClass().getClassLoader()
                        .getResourceAsStream("org/openscience/cdk/modeling/forcefield/data/mm2.prm");
                //logger.debug("ForceFieldConfigurator: open Force Field mm2 ... READY");
                mm2 = new MM2BasedParameterSetReader();
                mm2.setInputStream(ins);
                //logger.debug("ForceFieldConfigurator: mm2 set input stream ... READY");
                try {
                    this.setMM2Parameters(builder);
                } catch (Exception ex1) {
                    throw new CDKException("Problems with set MM2Parameters due to " + ex1.toString(), ex1);
                }
            } else if (ffName.equals("mmff94") || !check) {
                //logger.debug("ForceFieldConfigurator: open Force Field mmff94");
                //f = new File(mmff94File);
                //readFile(f);
                ins = this.getClass().getClassLoader()
                        .getResourceAsStream("org/openscience/cdk/modeling/forcefield/data/mmff94.prm");
                mmff94 = new MMFF94BasedParameterSetReader();

                mmff94.setInputStream(ins);
                try {
                    this.setMMFF94Parameters(builder);
                } catch (Exception ex2) {
                    throw new CDKException("Problems with set MM2Parameters due to" + ex2.toString(), ex2);
                }
            }
        }
        //throw new CDKException("Data file for "+ffName+" force field could not be found");
    }

    /**
     *  Sets the atomTypes attribute of the ForceFieldConfigurator object
     *
     * @param  atomtypes  The new atomTypes
     */
    public void setAtomTypes(List<IAtomType> atomtypes) {
        atomTypes = atomtypes;
    }

    /**
     *  Sets the parameters attribute of the ForceFieldConfigurator object
     *
     * @param  parameterset  The new parameter values
     */
    public void setParameters(Map<String, Object> parameterset) {
        parameterSet = parameterset;
    }

    /**
     *  Sets the parameters attribute of the ForceFieldConfigurator object, default is mm2 force field
     */
    public void setMM2Parameters(IChemObjectBuilder builder) throws CDKException {
        try {
            if (mm2 == null) mm2 = new MM2BasedParameterSetReader();
            mm2.readParameterSets(builder);
        } catch (Exception ex1) {
            throw new CDKException("Problem within readParameterSets due to:" + ex1.toString(), ex1);
        }
        parameterSet = mm2.getParamterSet();
        atomTypes = mm2.getAtomTypes();
    }

    public void setMMFF94Parameters(IChemObjectBuilder builder) throws Exception {
        if (mmff94 == null) mmff94 = new MMFF94BasedParameterSetReader();
        mmff94.readParameterSets(builder);
        parameterSet = mmff94.getParamterSet();
        atomTypes = mmff94.getAtomTypes();
    }

    /**
     *  Gets the atomTypes attribute of the ForceFieldConfigurator object
     *
     * @return    The atomTypes vector
     */
    public List<IAtomType> getAtomTypes() {
        return atomTypes;
    }

    /**
     *  Gets the parameterSet attribute of the ForceFieldConfigurator object
     *
     * @return    The parameterSet hashtable
     */
    public Map<String, Object> getParameterSet() {
        return this.parameterSet;
    }

    /**
     *  Find the atomType for a id
     *
     * @param  ID                           Atomtype id of the forcefield
     * @return                              The atomType
     * @exception  NoSuchAtomTypeException  atomType is not known.
     */
    private IAtomType getAtomType(String ID) throws NoSuchAtomTypeException {
        IAtomType at = null;
        for (int i = 0; i < atomTypes.size(); i++) {
            at = (IAtomType) atomTypes.get(i);
            if (at.getAtomTypeName().equals(ID)) {
                return at;
            }
        }
        throw new NoSuchAtomTypeException("AtomType " + ID + " could not be found");
    }

    /**
     *  Method assigns atom types to atoms (calculates sssr and aromaticity)
     *
     *@return                sssrf set
     *@exception  CDKException  Problems detecting aromaticity or making hose codes.
     */
    public IRingSet assignAtomTyps(IAtomContainer molecule) throws CDKException {
        IAtom atom = null;
        String hoseCode = "";
        HOSECodeGenerator hcg = new HOSECodeGenerator();
        int NumberOfRingAtoms = 0;
        IRingSet ringSetA = null;
        IRingSet ringSetMolecule = Cycles.sssr(molecule).toRingSet();
        boolean isInHeteroRing = false;
        try {
            AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(molecule);
            Aromaticity.cdkLegacy().apply(molecule);
        } catch (Exception cdk1) {
            throw new CDKException("AROMATICITYError: Cannot determine aromaticity due to: " + cdk1.getMessage(), cdk1);
        }

        for (int i = 0; i < molecule.getAtomCount(); i++) {
            atom = molecule.getAtom(i);
            if (ringSetMolecule.contains(atom)) {
                NumberOfRingAtoms = NumberOfRingAtoms + 1;
                atom.setFlag(CDKConstants.ISINRING, true);
                atom.setFlag(CDKConstants.ISALIPHATIC, false);
                ringSetA = ringSetMolecule.getRings(atom);
                RingSetManipulator.sort(ringSetA);
                IRing sring = (IRing) ringSetA.getAtomContainer(ringSetA.getAtomContainerCount() - 1);
                atom.setProperty("RING_SIZE", Integer.valueOf(sring.getRingSize()));
                isInHeteroRing = false;
                Iterator<IAtomContainer> containers = RingSetManipulator.getAllAtomContainers(ringSetA).iterator();
                while (!isInHeteroRing && containers.hasNext()) {
                    isInHeteroRing = isHeteroRingSystem(containers.next());
                }
            } else {
                atom.setFlag(CDKConstants.ISALIPHATIC, true);
                atom.setFlag(CDKConstants.ISINRING, false);
                isInHeteroRing = false;
            }
            atom.setProperty("MAX_BOND_ORDER", new Double(molecule.getMaximumBondOrder(atom).numeric()));

            try {
                hoseCode = hcg.getHOSECode(molecule, atom, 3);
                //logger.debug("HOSECODE GENERATION: ATOM "+i+" HoseCode: "+hoseCode+" ");
            } catch (CDKException ex1) {
                System.out.println("Could not build HOSECode from atom " + i + " due to " + ex1.toString());
                throw new CDKException("Could not build HOSECode from atom " + i + " due to " + ex1.toString(), ex1);
            }
            try {
                configureAtom(atom, hoseCode, isInHeteroRing);
            } catch (CDKException ex2) {
                System.out.println("Could not final configure atom " + i + " due to " + ex2.toString());
                throw new CDKException("Could not final configure atom due to problems with force field", ex2);
            }
        }

        //		IBond[] bond = molecule.getBonds();
        String bondType;
        for (IBond bond : molecule.bonds()) {

            //logger.debug("bond[" + i + "] properties : " + molecule.getBond(i).getProperties());
            bondType = "0";
            if (bond.getOrder() == IBond.Order.SINGLE) {
                if ((bond.getBegin().getAtomTypeName().equals("Csp2"))
                        && ((bond.getEnd().getAtomTypeName().equals("Csp2")) || (bond.getEnd().getAtomTypeName()
                                .equals("C=")))) {
                    bondType = "1";
                }

                if ((bond.getBegin().getAtomTypeName().equals("C="))
                        && ((bond.getEnd().getAtomTypeName().equals("Csp2")) || (bond.getEnd().getAtomTypeName()
                                .equals("C=")))) {
                    bondType = "1";
                }

                if ((bond.getBegin().getAtomTypeName().equals("Csp"))
                        && (bond.getEnd().getAtomTypeName().equals("Csp"))) {
                    bondType = "1";
                }
            }
            //			molecule.getBond(i).setProperty("MMFF94 bond type", bondType);
            bond.setProperty("MMFF94 bond type", bondType);
            //logger.debug("bond[" + i + "] properties : " + molecule.getBond(i).getProperties());
        }

        return ringSetMolecule;
    }

    /**
     *  Returns true if atom is in hetero ring system
     *
     *@param  ac  AtomContainer
     *@return     true/false
     */
    private boolean isHeteroRingSystem(IAtomContainer ac) {
        if (ac != null) {
            for (int i = 0; i < ac.getAtomCount(); i++) {
                if (!(ac.getAtom(i).getSymbol()).equals("H") && !(ac.getAtom(i).getSymbol()).equals("C")) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     *  Assigns an atom type to an atom
     *
     * @param  atom  The atom to be aasigned
     * @param  ID    the atom type id
     * @exception  NoSuchAtomTypeException  atomType is not known
     * @return       the assigned atom
     */
    private IAtom setAtom(IAtom atom, String ID) throws NoSuchAtomTypeException {
        IAtomType at = null;
        String key = "";
        List<?> data = null;
        Double value = null;

        at = getAtomType(ID);
        if (atom.getSymbol() == null) {
            atom.setSymbol(at.getSymbol());
        }
        atom.setAtomTypeName(at.getAtomTypeName());
        atom.setFormalNeighbourCount(at.getFormalNeighbourCount());
        key = "vdw" + ID;
        data = (List) parameterSet.get(key);
        value = (Double) data.get(0);
        key = "charge" + ID;
        if (parameterSet.containsKey(key)) {
            data = (List) parameterSet.get(key);
            value = (Double) data.get(0);
            atom.setCharge(value.doubleValue());
        }
        Object color = at.getProperty("org.openscience.cdk.renderer.color");
        if (color != null) {
            atom.setProperty("org.openscience.cdk.renderer.color", color);
        }
        if (at.getAtomicNumber() != 0) {
            atom.setAtomicNumber(at.getAtomicNumber());
        }
        if (at.getExactMass() > 0.0) {
            atom.setExactMass(at.getExactMass());
        }
        return atom;
    }

    public IAtom configureAtom(IAtom atom, String hoseCode, boolean _boolean) throws CDKException {
        if (ffName.equals("mm2")) {
            return configureMM2BasedAtom(atom, hoseCode, _boolean);
        } else if (ffName.equals("mmff94")) {
            return configureMMFF94BasedAtom(atom, hoseCode, _boolean);
        }
        return atom;
    }

    /**
     *  Configures an atom to a mm2 based atom type
     *
     * @param  atom              atom to be configured
     * @param  hoseCode          the 4 sphere hose code of the atom
     * @return                   atom
     * @exception  NoSuchAtomTypeException  atomType is not known
     */
    public IAtom configureMM2BasedAtom(IAtom atom, String hoseCode, boolean hetRing) throws NoSuchAtomTypeException {
        //logger.debug("CONFIGURE MM2 ATOM");
        List<Pattern> atomTypePattern = null;
        MM2BasedAtomTypePattern atp = new MM2BasedAtomTypePattern();
        atomTypePattern = atp.getAtomTypePatterns();
        Double d_tmp = null;
        Pattern p = null;
        String ID = "";
        boolean atomTypeFlag = false;

        if (atom instanceof IPseudoAtom) {
            return atom;
        }

        hoseCode = removeAromaticityFlagsFromHoseCode(hoseCode);

        String[] ids = {"C", "Csp2", "C=", "Csp", "HC", "O", "O=", "N", "Nsp2", "Nsp", "F", "CL", "BR", "I", "S", "S+",
                ">SN", "SO2", "SI", "LP", "HO", "CR3R", "HN", "HOCO", "P", "B", "BTET", "HN2", "C.", "C+", "GE", "SN",
                "PB", "SE", "TE", "D", "NPYD", "CE3R", "N+", "NPYL", "Oar", "Sthi", "N2OX", "HS", "=N=", "NO3", "OM",
                "HN+", "OR", "Car", "HE", "NE", "AR", "KR", "XE", "", "", "", "MG", "PTET", "FE", "FE", "NI", "NI",
                "CO", "CO", "", "", "OX", "OK", "C++", "N=C", "NPD+", "N+=", "N2OX"};

        for (int j = 0; j < atomTypePattern.size(); j++) {
            p = (Pattern) atomTypePattern.get(j);
            Matcher mat = p.matcher(hoseCode);
            if (mat.matches()) {
                ID = ids[j];
                //CHECK Rings 1,2,8,9? Thiole 44? AZO 9? Radical - ? Amid 23/enol 21?
                if (j == 0) {
                    //csp3
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(3))) {
                            ID = ids[21];
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))
                                && atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[1];
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[1];
                        }
                    }
                } else if (j == 1) {
                    //csp2
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))
                                && atom.getFlag(CDKConstants.ISAROMATIC)) {
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(3))) {
                            ID = ids[37];
                        } else {
                            ID = ids[1];
                        }
                    }
                    p = (Pattern) atomTypePattern.get(2);
                    //COOH
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[2];
                    }

                } else if (j == 5) {
                    //OH/Ether
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(3))) {
                            ID = ids[48];
                            //EPOXY
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))
                                && atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[40];
                        } else {
                            ID = ids[5];
                        }
                    }
                } else if (j == 7) {
                    //n sp3
                    if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[39];
                        }
                    }
                    //Amid
                    p = (Pattern) atomTypePattern.get(77);
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[8];
                    }
                } else if (j == 8) {
                    //nsp2
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))) {
                            ID = ids[36];
                        }
                    }
                    p = (Pattern) atomTypePattern.get(36);
                    //AZO
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[36];
                    }

                } else if (j == 43) {
                    //h thiol
                    d_tmp = (Double) atom.getProperty("MAX_BOND_ORDER");
                    if (d_tmp.doubleValue() > 1) {
                        ID = ids[4];
                    }
                } else if (j == 20) {
                    //h alcohol,ether
                    p = (Pattern) atomTypePattern.get(76);
                    //Enol
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[27];
                    }
                    p = (Pattern) atomTypePattern.get(23);
                    //COOH
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[23];
                    }
                } else if (j == 22) {
                    p = (Pattern) atomTypePattern.get(75);
                    //Amid
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[27];
                    }
                }

                atomTypeFlag = true;
                //logger.debug("Atom Symbol:" + atom.getSymbol() + " MATCH AtomType> " + ID + " HoseCode>" + hoseCode + " ");
                break;
            }//IF
        }//for end
        if (atomTypeFlag) {
            atomTypeFlag = false;
            return setAtom(atom, ID);
        } else {
            throw new NoSuchAtomTypeException("Atom is unkown: Symbol:" + atom.getSymbol()
                    + " does not MATCH AtomType. HoseCode:" + hoseCode);
        }
    }

    public String removeAromaticityFlagsFromHoseCode(String hoseCode) {
        String hosecode = "";
        for (int i = 0; i < hoseCode.length(); i++) {
            if (hoseCode.charAt(i) != '*') {
                hosecode = hosecode + hoseCode.charAt(i);
            }
        }
        return hosecode;
    }

    /**
     *  Configures an atom to a mmff94 based atom type
     *
     * @param  atom              atom to be configured
     * @param  hoseCode          the 4 sphere hose code of the atom
     * @return                   atom
     * @exception  NoSuchAtomTypeException  atomType is not known
     */
    public IAtom configureMMFF94BasedAtom(IAtom atom, String hoseCode, boolean isInHetRing)
            throws NoSuchAtomTypeException {
        //logger.debug("****** Configure MMFF94 AtomType ******");
        List<Pattern> atomTypePattern = null;
        MMFF94BasedAtomTypePattern atp = new MMFF94BasedAtomTypePattern();
        atomTypePattern = atp.getAtomTypePatterns();
        Pattern p = null;
        Pattern p2 = null;
        String ID = "";
        boolean atomTypeFlag = false;
        Matcher mat = null;
        Matcher mat2 = null;
        hoseCode = removeAromaticityFlagsFromHoseCode(hoseCode);

        String[] ids = {"C", "Csp2", "C=", "Csp", "CO2M", "CNN+", "C%", "CIM+", "CR4R", "CR3R", "CE4R", "Car", "C5A",
                "C5B", "C5", "HC", "HO", "HN", "HOCO", "HN=C", "HN2", "HOCC", "HOH", "HOS", "HN+", "HO+", "HO=+", "HP",
                "O", "O=", "OX", "OM", "O+", "O=+", "OH2", "Oar", "N", "N=C", "NC=C", "NSP", "=N=", "NAZT", "N+",
                "N2OX", "N3OX", "NC#N", "NO3", "N=O", "NC=O", "NSO", "N+=", "NCN+", "NGD+", "NR%", "NM", "N5M", "NPYD",
                "NPYL", "NPD+", "N5A", "N5B", "NPOX", "N5OX", "N5+", "N5", "S", "S=C", ">SN", "SO2", "SX", "SO2M",
                "=SO", "Sthi", "PTET", "P", "-P=C", "F", "CL", "BR", "I", "SI", "CL04", "FE+2", "FE+3", "F-", "CL-",
                "BR-", "LI+", "NA+", "K+", "ZN+2", "CA+2", "CU+1", "CU+2", "MG+2", "Du"};

        if (atom instanceof IPseudoAtom) {
            return atom;
        }

        for (int j = 0; j < atomTypePattern.size(); j++) {
            p = (Pattern) atomTypePattern.get(j);
            mat = p.matcher(hoseCode);
            if (mat.matches()) {
                ID = ids[j];
                if (j == 0) {//csp3
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        p = (Pattern) atomTypePattern.get(13);//c beta heteroaromatic ring
                        mat = p.matcher(hoseCode);
                        p2 = (Pattern) atomTypePattern.get(12);//c alpha heteroaromatic ring
                        mat2 = p2.matcher(hoseCode);
                        if (mat.matches() && isInHetRing && atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[13];
                        } else if (mat2.matches() && isInHetRing && atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[12];
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(3))
                                && !atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[9];//sp3 3mem rings
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(4))
                                && !atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[8];//sp3 4mem rings
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC) && isInHetRing
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[14];//C in het 5 ring
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC)) {
                            ID = ids[11];//C in benzene, pyroll
                        }
                    } else {
                        p = (Pattern) atomTypePattern.get(66);//S=C
                        mat = p.matcher(hoseCode);
                        if (mat.matches()) {
                            ID = ids[66];
                        }
                    }
                } else if (j == 1) {//csp2
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(4))
                                && !atom.getFlag(CDKConstants.ISAROMATIC) && !isInHetRing) {
                            ID = ids[29];//C= in 4 ring
                        }
                    }

                } else if (j == 2) {//csp2 C=Hetatom
                    if (atom.getFlag(CDKConstants.ISINRING) && isInHetRing && atom.getFlag(CDKConstants.ISAROMATIC)) {
                        ID = ids[12];
                    }

                } else if (j == 36) {//n sp3
                    //Amid
                    p = (Pattern) atomTypePattern.get(48);
                    mat = p.matcher(hoseCode);
                    if (mat.matches() && !atom.getFlag(CDKConstants.ISINRING)) {
                        ID = ids[48];
                    }

                    p = (Pattern) atomTypePattern.get(44);//sp3 n-oxide
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[44];
                    }

                    p = (Pattern) atomTypePattern.get(56);//npyd
                    mat = p.matcher(hoseCode);

                    if (atom.getFlag(CDKConstants.ISAROMATIC)) {//id in pyridin, pyrol etc...						if (mat.matches() && atom.getFlag(CDKConstants.ISAROMATIC) && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))){
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(6)) && mat.matches()) {
                            ID = ids[56];
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(5)) && mat.matches()) {
                            ID = ids[57];
                        } else {
                            ID = ids[64];
                        }
                    }

                    p = (Pattern) atomTypePattern.get(61);//npyd
                    mat = p.matcher(hoseCode);
                    if (atom.getFlag(CDKConstants.ISAROMATIC)) {//id in pyridin, pyrol etc...						if (mat.matches() && atom.getFlag(CDKConstants.ISAROMATIC) && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))){
                        if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(6)) && mat.matches()) {
                            ID = ids[61];
                        } else if (atom.getProperty("RING_SIZE").equals(Integer.valueOf(5)) && mat.matches()) {
                            ID = ids[62];
                        } else {
                            ID = ids[43];
                        }
                    }

                    p = (Pattern) atomTypePattern.get(45);//NC#N
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[45];
                    }

                } else if (j == 37) {//N=C n in imine
                    p = (Pattern) atomTypePattern.get(59);//n beta heteroaromatic ring
                    mat = p.matcher(hoseCode);
                    if (atom.getFlag(CDKConstants.ISINRING)) {
                        if (mat.matches() && isInHetRing && atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[59];
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))) {
                            ID = ids[56];
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[57];
                        }
                    }

                    p = (Pattern) atomTypePattern.get(43);//N2OX
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        if (atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))) {
                            ID = ids[61];//npox
                        } else if (mat.matches() && atom.getFlag(CDKConstants.ISAROMATIC)
                                && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                            ID = ids[62];//n5ox
                        } else {
                            ID = ids[43];
                        }
                    }

                } else if (j == 43) {//sp2 n oxide
                    if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                        ID = ids[62];
                    } else if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))) {
                        ID = ids[61];
                    }
                } else if (j == 40 || j == 41) {//n in c=n=n or terminal n in azido
                    if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                        ID = ids[59];//aromatic N 5R alpha
                    }
                } else if (j == 50) {//n+=
                    if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                        ID = ids[63];//n5+
                    } else if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(6))) {
                        ID = ids[58];//npd+
                    }
                } else if (j == 28) {//O ->furan
                    if (atom.getFlag(CDKConstants.ISINRING) && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atom.getProperty("RING_SIZE").equals(Integer.valueOf(5))) {
                        ID = ids[35];
                    }
                } else if (j == 16) {//H-Object-> enol
                    p = (Pattern) atomTypePattern.get(21);//enol
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[21];
                    }
                    p = (Pattern) atomTypePattern.get(18);//enol
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[18];
                    }

                } else if (j == 74) {//P
                    p = (Pattern) atomTypePattern.get(75);//-P=C
                    mat = p.matcher(hoseCode);
                    if (mat.matches()) {
                        ID = ids[75];
                    }
                }

                atomTypeFlag = true;
                //logger.debug("Atom Symbol:" + atom.getSymbol() + " MATCH AtomType> " + ID + " HoseCode>" + hoseCode + " ");
                break;
            }//IF
        }//for end
        if (atomTypeFlag) {
            atomTypeFlag = false;
            return setAtom(atom, ID);
        } else {
            throw new NoSuchAtomTypeException("Atom is unkown: Symbol:" + atom.getSymbol()
                    + " does not MATCH AtomType. HoseCode:" + hoseCode);
        }
    }
}
