class MMFF94AtomTypeMatcher implements IAtomTypeMatcher {

    @Override
    public IAtomType findMatchingAtomType(IAtomContainer atomContainer, IAtom atomInterface) throws CDKException {
        if (factory == null) {
            try {
                factory = AtomTypeFactory.getInstance("org/openscience/cdk/config/data/mmff94_atomtypes.xml",
                        atomContainer.getBuilder());
            } catch (Exception ex1) {
                logger.error(ex1.getMessage());
                logger.debug(ex1);
                throw new CDKException("Could not instantiate the AtomType list!", ex1);
            }
        }

        IAtom atom = atomInterface;
        //logger.debug("****** Configure MMFF94 AtomType via findMatching ******");
        //logger.debug(" Symbol:" + atom.getSymbol() +" HoseCode>" + atom.getSphericalMatcher() + " ");
        logger.debug(" Symbol:" + atom.getSymbol() + " HoseCode>" + atom.getProperty(CDKConstants.SPHERICAL_MATCHER)
                + " ");
        //System.out.print("IN MMFF94AtomTypeMatcher Symbol:" + atom.getSymbol() +" HoseCode>" + atom.getProperty(CDKConstants.SPHERICAL_MATCHER) + " ");

        if (atom instanceof IPseudoAtom) {
            return factory.getAtomTypes("DU")[0];
        }
        Pattern p1 = null;
        Pattern p2 = null;
        String ID = "";
        boolean atomTypeFlag = false;
        Matcher mat1 = null;
        Matcher mat2 = null;
        IBond.Order tmpMaxBondOrder;
        maxBondOrder = atomContainer.getMaximumBondOrder(atom);
        for (int j = 0; j < atomTypeIds.length; j++) {
            tmpMaxBondOrder = factory.getAtomType(atomTypeIds[j]).getMaxBondOrder();
            String atomSphericalMatcher = (String) factory.getAtomType(atomTypeIds[j]).getProperty(
                    CDKConstants.SPHERICAL_MATCHER);
            logger.debug(j + " ATOM TYPE " + tmpMaxBondOrder + " " + atomSphericalMatcher);
            p1 = Pattern.compile(atomSphericalMatcher);
            mat1 = p1.matcher((String) atom.getProperty(CDKConstants.SPHERICAL_MATCHER));
            if (mat1.matches()) {
                ID = atomTypeIds[j];
                Object property = atom.getProperty(CDKConstants.CHEMICAL_GROUP_CONSTANT);
                int atomChemGroupConstant = (Integer) property;
                Object ringSize = atom.getProperty(CDKConstants.PART_OF_RING_OF_SIZE);
                int atomRingSize = -1;
                if (ringSize != null) {
                    atomRingSize = (Integer) ringSize;
                }
                if (atomTypeIds[j].equals("C")) {
                    if (atomChemGroupConstant != -1) {//in Ring
                        if (ringSize != null && maxBondOrder == IBond.Order.SINGLE) {
                            if (atomRingSize == 3) {
                                ID = atomTypeIds[9];//sp3 3mem rings
                            } else if (atomRingSize == 4) {
                                ID = atomTypeIds[8];//sp3 4mem rings
                            }
                        } else {//sp2
                            String type13Matcher = getSphericalMatcher(atomTypeIds[13]);
                            p1 = Pattern.compile(type13Matcher);//C5B
                            mat1 = p1.matcher(atomSphericalMatcher);
                            String type12Matcher = getSphericalMatcher(atomTypeIds[12]);
                            p2 = Pattern.compile(type12Matcher);//C5A
                            mat2 = p2.matcher(atomSphericalMatcher);
                            if (mat1.matches() && atomChemGroupConstant % 2 == 0
                                    && atom.getFlag(CDKConstants.ISAROMATIC) && atomRingSize == 5) {
                                ID = atomTypeIds[13];
                            } else if (mat2.matches() && atomChemGroupConstant % 2 == 0
                                    && atom.getFlag(CDKConstants.ISAROMATIC) && atomRingSize == 5) {
                                ID = atomTypeIds[12];
                            } else if (atomChemGroupConstant % 2 == 0 && atom.getFlag(CDKConstants.ISAROMATIC)
                                    && atomRingSize == 5) {
                                ID = atomTypeIds[14];//C5 in het 5 ring
                            } else if (atom.getFlag(CDKConstants.ISAROMATIC)) {
                                ID = atomTypeIds[11];//Car in benzene, pyroll
                            }
                        }

                    } else {//not in Ring
                        p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[66]));//S=C
                        mat1 = p1.matcher(atomSphericalMatcher);
                        if (mat1.matches()) {
                            ID = atomTypeIds[66];//S=C
                        }
                    }

                } else if (atomTypeIds[j].equals("Csp2")) {
                    if (atomChemGroupConstant % 2 == 0 & atomRingSize == 4 & !atom.getFlag(CDKConstants.ISAROMATIC)) {
                        ID = atomTypeIds[10];//CE4R
                    }

                } else if (atomTypeIds[j].equals("C=")) {
                    if (atomChemGroupConstant % 2 == 0 && atom.getFlag(CDKConstants.ISAROMATIC)) {
                        ID = atomTypeIds[12];//C5A
                    }

                } else if (atomTypeIds[j].equals("N")) {
                    //Amid
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[48]));//NC=0
                    mat1 = p1.matcher(atomSphericalMatcher);
                    if (mat1.matches() && atomChemGroupConstant == -1) {
                        ID = atomTypeIds[48];//NC=O
                    }
                    //nsp3 oxide
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[44]));//sp3 n-oxide
                    mat1 = p1.matcher(atomSphericalMatcher);
                    if (mat1.matches() && maxBondOrder == tmpMaxBondOrder) {
                        ID = atomTypeIds[44];
                    }
                    //ring sytems
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[56]));
                    mat1 = p1.matcher(atomSphericalMatcher);

                    if (atomChemGroupConstant == 10) {
                        ID = atomTypeIds[56];
                    } else if (atomChemGroupConstant == 4) {
                        ID = atomTypeIds[57];
                    } else if (atomChemGroupConstant % 2 == 0 & atomRingSize == 5
                            & atom.getFlag(CDKConstants.ISAROMATIC)) {
                        ID = atomTypeIds[64];
                    }
                    //Nsp2-Oxides
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[61]));//npox
                    mat1 = p1.matcher(atomSphericalMatcher);

                    if (mat1.matches() && maxBondOrder == tmpMaxBondOrder) {
                        ID = atomTypeIds[43];
                    }
                    if (atom.getFlag(CDKConstants.ISAROMATIC)) {
                        if (mat1.matches() && atomChemGroupConstant == 12) {
                            ID = atomTypeIds[61];
                        } else if (mat1.matches() && atomRingSize == 5) {
                            ID = atomTypeIds[62];
                        }
                    }
                    //NC#N
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[45]));
                    mat1 = p1.matcher(getSphericalMatcher(atom));
                    if (mat1.matches()) {
                        ID = atomTypeIds[45];
                    }

                } else if (atomTypeIds[j].equals("N=C")) {
                    //n beta heteroaromatic ring
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[59]));
                    mat1 = p1.matcher(getSphericalMatcher(atom));
                    if (atomChemGroupConstant != -1) {
                        if (mat1.matches() && atomChemGroupConstant % 2 == 0 && atom.getFlag(CDKConstants.ISAROMATIC)
                                && atomRingSize == 5) {
                            ID = atomTypeIds[59];//N5A
                        } else if (atomChemGroupConstant == 10) {//NPYD
                            ID = atomTypeIds[56];
                        } else if (atomChemGroupConstant == 4) {//NPYL
                            ID = atomTypeIds[57];
                        }
                    }
                    //N2OX
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[43]));
                    mat1 = p1.matcher(getSphericalMatcher(atom));
                    if (mat1.matches()) {
                        if (atomChemGroupConstant == 10) {
                            ID = atomTypeIds[61];//npox
                        } else if (atom.getFlag(CDKConstants.ISAROMATIC) && atomRingSize == 5) {
                            ID = atomTypeIds[62];//n5ox
                        } else {
                            ID = atomTypeIds[43];//n2ox
                        }
                    }

                } else if (atomTypeIds[j].equals("N2OX")) {
                    //NO3
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[46]));
                    mat1 = p1.matcher(getSphericalMatcher(atom));
                    if (mat1.matches() && atomChemGroupConstant == -1) {
                        ID = atomTypeIds[46];//NO3
                    }
                    if (atomChemGroupConstant == 12) {
                        ID = atomTypeIds[61];//NPOX
                    } else if (atomChemGroupConstant != -1 && atom.getFlag(CDKConstants.ISAROMATIC)
                            && atomRingSize == 5) {
                        ID = atomTypeIds[62];//N5OX
                    }

                } else if (atomTypeIds[j].equals("=N=") || atomTypeIds[j].equals("NAZT")) {
                    if (atomChemGroupConstant != -1 && atom.getFlag(CDKConstants.ISAROMATIC) && atomRingSize == 5) {
                        ID = atomTypeIds[59];//aromatic N5A
                    }

                } else if (atomTypeIds[j].equals("N+=")) {
                    if (atomChemGroupConstant != -1 && atom.getFlag(CDKConstants.ISAROMATIC) && atomRingSize == 5) {
                        ID = atomTypeIds[63];//n5+
                    } else if (atomChemGroupConstant == 12) {
                        ID = atomTypeIds[58];//npd+
                    }

                } else if (atomTypeIds[j].equals("O")) {
                    if (atomChemGroupConstant == 6) {
                        ID = atomTypeIds[35];//Oar
                    }

                } else if (atomTypeIds[j].equals("HO")) {
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[21]));
                    mat1 = p1.matcher(atomSphericalMatcher);
                    if (mat1.matches()) {
                        ID = atomTypeIds[21];//HOCC
                    }
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[18]));
                    mat1 = p1.matcher(atomSphericalMatcher);
                    if (mat1.matches()) {
                        ID = atomTypeIds[18];//HOCO
                    }

                } else if (atomTypeIds[j].equals("P")) {
                    p1 = Pattern.compile(getSphericalMatcher(atomTypeIds[75]));
                    mat1 = p1.matcher(atomSphericalMatcher);
                    if (mat1.matches()) {
                        ID = atomTypeIds[75];//-P=C
                    }
                } else if (atomTypeIds[j].equals("S")) {
                    if (atomRingSize == 5 && atom.getFlag(CDKConstants.ISAROMATIC)) {
                        ID = atomTypeIds[72];//Sthiophen
                    }
                } else if (atomTypeIds[j].equals("HC")) {
                    p1 = Pattern.compile(getSphericalMatcher("HP"));
                    mat1 = p1.matcher((String) atom.getProperty(CDKConstants.SPHERICAL_MATCHER));
                    if (mat1.matches()) {
                        ID = "HP";
                    }
                }

                atomTypeFlag = true;
                logger.debug(" MATCH AtomTypeID:" + j + " " + ID);
                break;
            }//IF
        }//for end
        if (atomTypeFlag) {
            atomTypeFlag = false;
            //System.out.println(" "+ID);
            return factory.getAtomType(ID);
        } else {
            return factory.getAtomType("DU");
        }
    }

    public  MMFF94AtomTypeMatcher();

}