/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Christian Rufener
 */

package com.actelion.research.chem.reaction;

import com.actelion.research.chem.ExtendedMolecule;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.StereoMolecule;
import com.actelion.research.chem.io.RXNFileParser;

public class ReactionIndexer
{
    private static boolean debug = false;
/*
    public static final int BOND_BREAK_1 = 0x0001;
    public static final int BOND_BREAK_2 = 0x0002;
    public static final int BOND_BREAK_3 = 0x0004;
    public static final int BOND_BREAK_M = 0x0008;

    public static final int BOND_CREATE_1 = 0x0010;
    public static final int BOND_CREATE_2 = 0x0020;
    public static final int BOND_CREATE_3 = 0x0040;
    public static final int BOND_CHANGE_13 = 0x0080;

    public static final int BOND_CHANGE_12 = 0x0100;
    public static final int BOND_CHANGE_21 = 0x0200;
    public static final int BOND_CHANGE_23 = 0x0400;
    public static final int BOND_CHANGE_32 = 0x0800;
    public static final int BOND_CHANGE_31 = 0x1000;
    public static final int BOND_CHANGE_A2 = 0x2000;
    public static final int BOND_CHANGE_A1 = 0x4000;
    public static final int BOND_CHANGE_O = 0x8000;

    public static final int ATOM_C_C = 0x00010000;
    public static final int ATOM_C_O = 0x00020000;
    public static final int ATOM_C_N = 0x00040000;
    public static final int ATOM_C_X = 0x00080000;
    public static final int ATOM_C_M = 0x00100000;
*/

    private static final byte CR_ONLY = 0;
    // Carbon Create Keys
    private static final byte CR_CC1 = 1;
    private static final byte CR_CC2 = 2;
    private static final byte CR_CC3 = 3;
    private static final byte CR_CCA = 4;
    private static final byte CR_CN1 = 5;
    private static final byte CR_CN2 = 6;
    private static final byte CR_CN3 = 7;
    private static final byte CR_CO1 = 8;
    private static final byte CR_CO2 = 9;
    private static final byte CR_CCl = 10;
    private static final byte CR_CX = 11;

    // Nitrogen Create Keys
    private static final byte CR_NN1 = 12;
    private static final byte CR_NN2 = 13;
    private static final byte CR_NO1 = 14;
    private static final byte CR_NO2 = 15;

    // Change Carbon Keys
    private static final byte CH_CCSD = 16;
    private static final byte CH_CCDS = 17;
    private static final byte CH_CCST = 18;
    private static final byte CH_CCTS = 19;
    private static final byte CH_CCDT = 20;
    private static final byte CH_CCTD = 21;
    private static final byte CH_CCSA = 22;
    private static final byte CH_CCAS = 23;
    private static final byte CH_CCDA = 24;
    private static final byte CH_CCAD = 25;
    private static final byte CH_CCTA = 26;
    private static final byte CH_CCAT = 27;

    private static final byte CH_CNSD = 28;
    private static final byte CH_CNDS = 29;
    private static final byte CH_CNST = 30;
    private static final byte CH_CNTS = 31;
    private static final byte CH_CNDT = 32;
    private static final byte CH_CNTD = 33;
    private static final byte CH_CNSA = 34;
    private static final byte CH_CNAS = 35;
    private static final byte CH_CNDA = 36;
    private static final byte CH_CNAD = 37;

    private static final byte CH_COSD = 38;
    private static final byte CH_CODS = 39;


    private static final byte DEL_ONLY = 40;
    private static final byte DEL_CCS = 41;
    private static final byte DEL_CCD = 42;
    private static final byte DEL_CCT = 43;
    private static final byte DEL_CCA = 44;
    private static final byte DEL_CNS = 45;
    private static final byte DEL_CND = 46;
    private static final byte DEL_CNT = 47;
    private static final byte DEL_COS = 48;
    private static final byte DEL_COT = 49;
    private static final byte DEL_CF = 50;
    private static final byte DEL_CCl = 51;
    private static final byte DEL_CBR = 52;
    private static final byte DEL_CX = 53;

    // Nitrogen Create Keys
    private static final byte DEL_NN1 = 54;
    private static final byte DEL_NN2 = 55;
    private static final byte DEL_NO1 = 56;
    private static final byte DEL_NO2 = 57;

    private static final byte CH_ONLY = 58;

    public static final byte NUMKEYS = 1 + CH_ONLY;

    public static final int NITROGEN = 7;
    public static final int OXYGEN = 8;
    public static final int FLUORINE = 9;
    public static final int CHLORINE = 17;
    public static final int BROMINE = 35;
    public static final int IODINE = 53;
    public static final int ASTATINE = 85;

    private static final int CARBON = 6;

    private static final String[] KEY_STRING = {
        "CR_ONLY",
        "CR_CC1",
        "CR_CC2",
        "CR_CC3",
        "CR_CCA",
        "CR_CN1",
        "CR_CN2",
        "CR_CN3",
        "CR_CO1",
        "CR_CO2",
        "CR_CCl",
        "CR_CX",
        "CR_NN1",
        "CR_NN2",
        "CR_NO1",
        "CR_NO2",
        "CH_CCSD",
        "CH_CCDS",
        "CH_CCST",
        "CH_CCTS",
        "CH_CCDT",
        "CH_CCTD",
        "CH_CCSA",
        "CH_CCAS",
        "CH_CCDA",
        "CH_CCAD",
        "CH_CCTA",
        "CH_CCAT",
        "CH_CNSD",
        "CH_CNDS",
        "CH_CNST",
        "CH_CNTS",
        "CH_CNDT",
        "CH_CNTD",
        "CH_CNSA",
        "CH_CNAS",
        "CH_CNDA",
        "CH_CNAD",
        "CH_COSD",
        "CH_CODS",
        "DEL_ONLY",
        "DEL_CC1",
        "DEL_CC2",
        "DEL_CC3",
        "DEL_CCA",
        "DEL_CN1",
        "DEL_CN2",
        "DEL_CN3",
        "DEL_CO1",
        "DEL_CO2",
        "DEL_CF ",
        "DEL_CCl",
        "DEL_CBR",
        "DEL_CX ",
        "DEL_NN1",
        "DEL_NN2",
        "DEL_NO1",
        "DEL_NO2",
        "CH_ONLY",
    };

    private byte[] rxnKeys = null;

    public ReactionIndexer(boolean debug)
    {
        this.debug = debug;
        init();
    }

    public ReactionIndexer()
    {
        this(false);
    }

    private void init()
    {
        rxnKeys = new byte[NUMKEYS];
    }

    public String getKeysString(Reaction rxn)
    {
        init();
        generateKeys(rxn);
        return getKeysString();

    }

    public static String getKeyName(int key)
    {
        return KEY_STRING[key];
    }
    public byte[] getKeys(Reaction rxn)
    {
        init();
        generateKeys(rxn);
        byte[] ret = new byte[rxnKeys.length];
        System.arraycopy(rxnKeys, 0, ret, 0, rxnKeys.length);
        return ret;
    }

    public boolean hasKeys()
    {
        for (int k : rxnKeys) {
            if (k != 0) {
                return true;
            }
        }
        return false;
    }

    private void addKey(int index)
    {
        if (index >= 0 && rxnKeys[index] < Byte.MAX_VALUE) {
            rxnKeys[index]++;
        }
    }

    private void initKeys()
    {
        init();
    }


    public String getFoundKeys()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < NUMKEYS; i++) {
            if (rxnKeys[i] > 0) {
                sb.append(String.format("%s (%d)\n",KEY_STRING[i],(int)rxnKeys[i]));
            }
        }
        return sb.toString();

    }
    public static byte[] getKeysFromString(String s)
    {
        byte[] ret = new byte[NUMKEYS];
        if (s != null) {
            if (s.length() != NUMKEYS)
                throw new RuntimeException("Invalid KeyString Length");
            for (int i = 0; i < NUMKEYS; i++) {
                ret[i] = (byte)(s.charAt(i)-'0');
            }
        }

        return ret;
    }


    public static String getKeysString(byte[] keys)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < NUMKEYS; i++) {
            sb.append(String.format("%1d", (int) keys[i]));
        }
        return sb.toString();
    }

    private String getKeysString()
    {
        return getKeysString(rxnKeys);
    }

    private int addCreateKey(int bondtype, int atomSymbol1, int atomSymbol2)
    {
        int key = CR_ONLY;
        int a1 = atomSymbol1;
        int a2 = atomSymbol2;
        if (atomSymbol2 < atomSymbol1) {
            a1 = atomSymbol2;
            a2 = atomSymbol1;
        }
        switch (a1) {
            case CARBON:
                key = getCarbonCreateKey(bondtype, a2);
                break;
            case NITROGEN:
                key = getNitroCreateKey(bondtype, a2);
                break;

        }
        return key;
    }

    private int getCarbonCreateKey(int bondtype, int atomNo)
    {
        int key = CR_ONLY;
        switch (atomNo) {
            case CARBON:
                key = getCreateCarbonCarbonKey(bondtype);
                break;

            case NITROGEN:
                key = getCreateCarbonNitroKey(bondtype);
                break;

            case OXYGEN:
                key = getCreateCarbonOxygenKey(bondtype);
                break;

            case CHLORINE:
                key = CR_CCl;
                break;

            case FLUORINE:
            case BROMINE:
            case IODINE:
            case ASTATINE:
                key = CR_CX;
                break;

        }
        return key;
    }

    private int getNitroCreateKey(int bondtype, int atomNo)
    {
        int key = CR_ONLY;
        switch (atomNo) {
            case NITROGEN:
                if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                    key = CR_NN1;
                } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                    key = CR_NN2;
                }
                break;

            case OXYGEN:
                if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                    key = CR_NO1;
                } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                    key = CR_NO2;
                }
                break;
        }
        return key;
    }

    private int getCreateCarbonCarbonKey(int bondtype)
    {
        if (bondtype == Molecule.cBondTypeDelocalized) {
            return CR_CCA;
        } else if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            return CR_CC1;
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            return CR_CC2;
        } else if ((bondtype & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            return CR_CC3;
        }
        return CR_ONLY;
    }

    private int getCreateCarbonNitroKey(int bondtype)
    {

        if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            return CR_CN1;
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            return CR_CN2;
        } else if ((bondtype & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            return CR_CN3;
        }
        return CR_ONLY;
    }

    private int getCreateCarbonOxygenKey(int bondtype)
    {
        if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            return CR_CO1;
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            return CR_CO2;
        }
        return CR_ONLY;
    }

    ///////// Change////////
    private void addChangeKey(int atomNo1, int atomNo2, int bt1, int bt2)
    {
        addKey(CH_ONLY);
        int a1 = atomNo1;
        int a2 = atomNo2;
        if (atomNo2 < atomNo1) {
            a1 = atomNo2;
            a2 = atomNo1;
        }
        switch (a1) {
            case CARBON:
                addCarbonChangeKey(a2, bt1, bt2);
                break;
        }
        //return key;
    }

    private void addCarbonChangeKey(int atomNo, int bt1, int bt2)
    {
        int key = CH_ONLY;
        switch (atomNo) {
            case CARBON:
                addChangeCarbonCarbonKey(bt1, bt2);
                break;

            case NITROGEN:
                addChangeCarbonNitroKey(bt1, bt2);
                break;

            case OXYGEN:
                addChangeCarbonOxygenKey(bt1, bt2);
                break;

        }
//        return key;
    }


    private void addChangeCarbonCarbonKey(int bt1, int bt2)
    {
//        int key = CH_ONLY;
        if (bt1 == Molecule.cBondTypeDelocalized) {
            if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CCAS);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CCAD);
            } else if ((bt2 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
                addKey(CH_CCAT);
            }
        } else if ((bt1 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            if (bt2 == Molecule.cBondTypeDelocalized) {
                addKey(CH_CCSA);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CCSD);
            } else if ((bt2 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
                addKey(CH_CCST);
            }
        } else if ((bt1 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            if (bt2 == Molecule.cBondTypeDelocalized) {
                addKey(CH_CCDA);
            } else if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CCDS);
            } else if ((bt2 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
                addKey(CH_CCDT);
            }
        } else if ((bt1 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            if (bt2 == Molecule.cBondTypeDelocalized) {
                addKey(CH_CCTA);
            } else if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CCTS);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CCTD);
            }
        }

//        return key;
    }

    private void addChangeCarbonNitroKey(int bt1, int bt2)
    {
//        int addKey(CH_ONLY;
        if (bt1 == Molecule.cBondTypeDelocalized) {
            if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CNAS);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CNAD);
            }
        } else if ((bt1 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            if (bt2 == Molecule.cBondTypeDelocalized) {
                addKey(CH_CNSA);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CNSD);
            } else if ((bt2 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
                addKey(CH_CNST);
            }
        } else if ((bt1 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            if (bt2 == Molecule.cBondTypeDelocalized) {
                addKey(CH_CNDA);
            } else if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CNDS);
            } else if ((bt2 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
                addKey(CH_CNDT);
            }
        } else if ((bt1 & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CNTS);
            } else if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_CNTD);
            }
        }

//        return key;
    }

    private void addChangeCarbonOxygenKey(int bt1, int bt2)
    {
//        int addKey(CH_ONLY;
        if ((bt1 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            if ((bt2 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                addKey(CH_COSD);
            }
        } else if ((bt1 & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            if ((bt2 & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                addKey(CH_CODS);
            }
        }
        //return key;
    }


    /*
        private int getChangeKey(int bt1, int bt2)
        {
            int addKey(-1;
            if (bt1 == Molecule.cBondTypeDelocalized) {
                if ((bt2 & Molecule.cBondTypeSingle) ==Molecule.cBondTypeSingle) {
                    addKey(CH_AS;
                }else if ((bt2 & Molecule.cBondTypeDouble) ==Molecule.cBondTypeDouble) {
                    addKey(CH_AD;
                }else if ((bt2 & Molecule.cBondTypeTriple) ==Molecule.cBondTypeTriple) {
                    addKey(CH_AT;
                }
            } else if ((bt1 & Molecule.cBondTypeSingle) ==Molecule.cBondTypeSingle) {
                if (bt2 == Molecule.cBondTypeDelocalized) {
                    addKey(CH_SA;
                } else if ((bt2 & Molecule.cBondTypeDouble) ==Molecule.cBondTypeDouble) {
                    addKey(CH_SD;
                }else if ((bt2 & Molecule.cBondTypeTriple) ==Molecule.cBondTypeTriple) {
                    addKey(CH_ST;
                }
            } else if ((bt1 & Molecule.cBondTypeDouble) ==Molecule.cBondTypeDouble) {
                if (bt2 == Molecule.cBondTypeDelocalized) {
                    addKey(CH_DA;
                } else if ((bt2 & Molecule.cBondTypeSingle) ==Molecule.cBondTypeSingle) {
                    addKey(CH_DS;
                }else if ((bt2 & Molecule.cBondTypeTriple) ==Molecule.cBondTypeTriple) {
                    addKey(CH_DT;
                }
            } else if ((bt1 & Molecule.cBondTypeTriple) ==Molecule.cBondTypeTriple) {
                if (bt2 == Molecule.cBondTypeDelocalized) {
                    addKey(CH_TA;
                } else if ((bt2 & Molecule.cBondTypeSingle) ==Molecule.cBondTypeSingle) {
                    addKey(CH_TS;
                }else if ((bt2 & Molecule.cBondTypeDouble) ==Molecule.cBondTypeDouble) {
                    addKey(CH_TD;
                }
            }

            return key;
        }
    */
    private void addDeleteKey(int bondtype, int atomNo1, int atomNo2)
    {
        addKey(DEL_ONLY);
        int a1 = atomNo1;
        int a2 = atomNo2;
        if (atomNo2 < atomNo1) {
            a1 = atomNo2;
            a2 = atomNo1;
        }
        switch (a1) {
            case CARBON:
                addCarbonDeleteKey(bondtype, a2);
                break;
            case NITROGEN:
                addNitroDeleteKey(bondtype, a2);
                break;

        }
    }

    private void addCarbonDeleteKey(int bondtype, int atomNo)
    {
//          int addKey(DEL_ONLY;
        switch (atomNo) {
            case CARBON:
                addDeleteCarbonCarbonKey(bondtype);
                break;

            case NITROGEN:
                addDeleteCarbonNitroKey(bondtype);
                break;

            case OXYGEN:
                addDeleteCarbonOxygenKey(bondtype);
                break;
            case FLUORINE:
            case CHLORINE:
            case 35:
            case IODINE:
                addHalogenDeleteKey(atomNo);
                break;

        }
//        addKey();
    }

    private void addDeleteCarbonCarbonKey(int bondtype)
    {
        if (bondtype == Molecule.cBondTypeDelocalized) {
            addKey(DEL_CCA);
        } else if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            addKey(DEL_CCS);
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            addKey(DEL_CCD);
        } else if ((bondtype & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            addKey(DEL_CCT);
        }
        //addKey(DEL_ONLY;
    }

    private void addDeleteCarbonNitroKey(int bondtype)
    {

        if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            addKey(DEL_CNS);
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            addKey(DEL_CND);
        } else if ((bondtype & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            addKey(DEL_CNT);
        }
        //return DEL_ONLY;
    }

    private void addDeleteCarbonOxygenKey(int bondtype)
    {
        if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            addKey(DEL_COS);
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            addKey(DEL_COT);
        }
        //return DEL_ONLY;
    }

    private void addNitroDeleteKey(int bondtype, int atomNo)
    {
//        int addKey(DEL_ONLY;
        switch (atomNo) {
            case NITROGEN:
                if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                    addKey(DEL_NN1);
                } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                    addKey(DEL_NN2);
                }
                break;

            case 8:
                if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
                    addKey(DEL_NO1);
                } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
                    addKey(DEL_NO2);
                }
                break;
        }
//        return key;
    }

    private void addHalogenDeleteKey(int atomNo)
    {
        addKey(DEL_CX);
        switch (atomNo) {
            case FLUORINE:
                addKey(DEL_CF);
                break;
            case CHLORINE:
                addKey(DEL_CCl);
                break;
            case BROMINE:
                addKey(DEL_CBR);
                break;
            case IODINE:
                break;
        }
        //return key;
    }


/*
    C1             C5                                  C1        C5
      \           /                                      \      /
       C2-Cl    N4                 -->                    C2 - N4
      /           \                                      /      \
     C3            C6                                   C3       C6

    C1                                                 C1
      \                                                  \
       C2 = O3                     -->                   C2 - 03
      /                                                  /
     C3                                                 C3

    C1         C5                                       C1            C5
      \       /                                           \          /
       C2 - N4                     -->                    C2       N4
      /      \                                           /           \
     C3       C6                                        C3           C6


    C1         C         C5                            C1         C5
      \       /        /                                 \       /
       C2 - N        N4            -->                    C2 - N4
      /      \         \                                 /       \
     C3       C         C6                             C3         C6


*/
//  C1 -> Map No = 1, pAtomNo = x, cConntectedAtoms = 1, pNumConnectedAtoms=1
//       cBondAtom = (C2), bondType = 1, cOtherMapNo = 2, pBondType=1 = > NO CHANGE
//  C2 -> Map No = 2, pAtomNo = x, cConntextedAtoms = 3, pNumConnectedAtoms = 3
//        C1 == OK
//        C3 == OK
//       cBondAtom (Cl),cBondType=1,cOtherMapNo=0
//

    /*
                            // For all atoms connected to Component Atoms (cAtomNo)
                            for (int cbi = 0; cbi < cNumConnectedAtoms; cbi++) {
                                // get the other atom at this bond
                                int cBondAtom = source.getConnAtom(cAtomNo, cbi);
                                // Get the bond index from these two atoms
                                int sourceBondIndex = source.getConnBond(cAtomNo, cbi);
                                if (cVisitedBonds[sourceBondIndex])
                                    continue;
                                // Find the mapping number of the bond's other end
                                int cOtherMapNo = source.getAtomMapNo(cBondAtom);
                                // get the bond type of this atom
                                int cBondType = getBondType(source, sourceBondIndex);
                                ////sb.append(String.format("Source Bond From %d(%d) - %d(%d) \n",cAtomNo,cMapNo,cBondAtom,cOtherMapNo));

                                cVisitedBonds[sourceBondIndex] = true;
                                // the other end is mapped
                                if (cOtherMapNo != 0) {
                                    // find the cont type of the two mapped atoms on the preoduct
                                    int pBondType = getMappedBondType(target, cMapNo, cOtherMapNo);
                                    //sb.append(String.format("Proiduct Bond Type (%d) - (%d) = %d \n",cMapNo,cOtherMapNo,pBondType));
                                    // If there's no bond, then the bond has been brokwn
                                    if (pBondType == -1) {
                                        if (!reverse) {
                                            System.out.println(String.format("###BOND_BREAK(1) at %d-%d \n", cOtherMapNo, cMapNo));
                                            int key = addDeleteKey(cBondType, source.getAtomicNo(cAtomNo), source.getAtomicNo(cBondAtom));
                                            if (debug)
                                                System.out.println("Delete Key is " + key);
                                            addKey(key);
                                            addKey(DEL_ONLY);
                                        } else {
                                            System.out.println(String.format("###BOND_CREATE(1) at %d-%d \n", cOtherMapNo, cMapNo));
                                            int key = addCreateKey(cBondType, source.getAtomicNo(cAtomNo), source.getAtomicNo(cBondAtom));
                                            addKey(key);
                                            addKey(CR_ONLY);
                                        }
                                        if (debug)
                                            sb.append(String.format("Bond %d %s-%s\n", sourceBondIndex, source.getAtomLabel(cAtomNo), source.getAtomLabel(cBondAtom)));
                                        // BOND_BREAK;
                                        // If the bond type is the same, nothing happend
                                    } else if (cBondType == pBondType) {
                                        ;
                                        // the bond type is different, so it's a change
                                    } else {
                                        if (!reverse) {
                                            System.out.println(String.format("###BOND_CHANGE(1) Type=%d Map %d-%d \n", pBondType, cOtherMapNo, cMapNo));
                                            int key = getChangeKey(source.getAtomicNo(cAtomNo), source.getAtomicNo(cBondAtom), cBondType, pBondType);
                                            addKey(key);
                                            addKey(CH_ONLY);
                                        }
                                        // BOND_CHANGE
                                    }

                                } else {
                                    System.out.println(String.format("not found other map no %d %d", listIndex, cMapNo));
                                    //if (!cVisitedBonds[sourceBondIndex]) {
                                    //    System.out.println(String.format("## UNDEFINED",listIndex,cMapNo));
                                    //}
                                    // Does this bond break
                                }
                            }
                            if (!reverse) {
                                if (cNumConnectedAtoms < pNumConnectedAtoms) {
                                    System.out.println(String.format("searching backward? %d/%d (%d)", cNumConnectedAtoms, pNumConnectedAtoms, cMapNo));
                                    int bondIndex = findDeletedBond(target, pAtomNo, source, cAtomNo);
                                    if (bondIndex != -1 && !cVisitedBonds[bondIndex]) {
                                        int ba = target.getConnAtom(pAtomNo, bondIndex);
                                        int type = target.getAtomicNo(ba);
                                        int bt = getBondType(target, bondIndex);
                                        System.out.println(String.format("### BOND_CREATE(2) at Target Atom %d(%d)[%d] from Source Atom %d(%d)", pAtomNo, cMapNo, type, cAtomNo, cMapNo));
                                        // BOND_CREATE
                                        int key = addCreateKey(bt, target.getAtomicNo(pAtomNo), target.getAtomicNo(ba));
                                        addKey(key);
                                        addKey(CR_ONLY);
                                    }
                                } else if (cNumConnectedAtoms > pNumConnectedAtoms) {
                                    System.out.println(String.format("searching forward? %d/%d (%d)", cNumConnectedAtoms, pNumConnectedAtoms, cMapNo));
                                    int bondIndex = findDeletedBond(source, cAtomNo, target, pAtomNo);
                                    if (bondIndex != -1 && !cVisitedBonds[bondIndex]) {
                                        int ba = source.getConnAtom(cAtomNo, bondIndex);
                                        int type = source.getAtomicNo(ba);
                                        int bt = getBondType(target, bondIndex);
                                        System.out.println(String.format("### BOND_DELETE(2) at Target Atom %d(%d)[%d] from Source Atom %d(%d)", pAtomNo, cMapNo, type, cAtomNo, cMapNo));
                                        // BOND_CREATE
                                        int key = addDeleteKey(bt, source.getAtomicNo(cAtomNo), source.getAtomicNo(ba));
                                        addKey(key);
                                        addKey(DEL_ONLY);
                                    }
                                }
                            }
                        } else {
                        }
                    }
                }
            } else {

            }
            sb.append(getKeysString());
            return sb.toString();
        }
    */
    private int findDeletedBond(ExtendedMolecule source, int sourceAtom, ExtendedMolecule target, int targetAtom)
    {
        int sourceNumConnectedAtoms = source.getConnAtoms(sourceAtom);
        int originAtomType = source.getAtomicNo(sourceAtom);
        int targetNumConnectedAtoms = target.getConnAtoms(targetAtom);
        boolean visitedSourceBonds[] = new boolean[sourceNumConnectedAtoms];
        if (sourceNumConnectedAtoms > targetNumConnectedAtoms) {
            if (debug) {
                System.out.println(String.format("findDeletedBond() walk source atom %d", sourceAtom));
            }
// Walk all bonds at this atom
            boolean visitedTargetBonds[] = new boolean[targetNumConnectedAtoms];
            for (int i = 0; i < sourceNumConnectedAtoms; i++) {
                if (!visitedSourceBonds[i]) {
                    // This is the atom of the bond
                    int sourceBondAtom = source.getConnAtom(sourceAtom, i);
// And this is the bond
                    int sourceBond = source.getConnBond(sourceAtom, i);
// The bond type
                    int sourceBondType = getBondType(source, sourceBond);
// The atom type
                    int sourceAtomType = source.getAtomicNo(sourceBondAtom);
                    if (debug) {
                        System.out.println(String.format("findDeletedBond() walk source bond atom %d bond %d type %d atomtype %d", sourceBondAtom, i, sourceBondType, sourceAtomType));
                    }
                    for (int j = 0; j < targetNumConnectedAtoms; j++) {
                        if (!visitedTargetBonds[j]) {
                            int bondAtom = target.getConnAtom(targetAtom, j);
                            int bond = target.getConnBond(targetAtom, j);
                            int targetBondType = getBondType(target, bond);
                            int targetAtomType = target.getAtomicNo(bondAtom);
                            if (debug) {
                                System.out.println(String.format("findDeletedBond() walk checking bond %d->%d (%d/%d) %d/%d", sourceAtomType, targetAtomType, i, j, sourceBondType, targetBondType));
                            }
                            if (sourceAtomType == targetAtomType && sourceBondType == targetBondType) {
                                visitedSourceBonds[i] = true;
                                visitedTargetBonds[j] = true;
                                System.out.println("Found Bond");
                                break;
                            }
                        }
                    }
                    if (!visitedSourceBonds[i]) {
                        if (debug) {
                            System.out.println(String.format("findDeletedBond() walk source bond not found atom %d bond %d type %d atomtype %d->%d",

                                sourceBondAtom, i, sourceBondType, originAtomType, sourceAtomType));
                        }
                    }
                }
            }
            for (int i = 0; i < sourceNumConnectedAtoms; i++) {
                if (!visitedSourceBonds[i]) {
                    if (debug) {
                        System.out.println(String.format("Did not find bond: %d(%d)", sourceAtom, i));
                    }
// this bond has not been seen, so it's breacking
                    return i;
                }
            }
        }
        return -1;
    }

    private boolean findBond(ExtendedMolecule mol, int atomIndex, int atomType, int bondType)
    {
        int pNumConnectedAtoms = mol.getConnAtoms(atomIndex);
        return false;
    }

    /*
        ExtendedMolecule findMappedAtomMol(int mapno)
        {
            ExtendedMolecule ret = null;
            int prod = rxn.getProducts();
            for (int i = 0; i < prod; i++) {
                ExtendedMolecule m = rxn.getMolecule(i);
                for (int a = 0; a < m.getAllAtoms(); a++) {
                    if (m.getAtomMapNo(a) == mapno) {
                        ret = m;
                        break;
                    }
                }
            }
            return ret;
        }
    */
    int getMappedAtom(ExtendedMolecule m, int map1)
    {
        int a1 = -1;
        for (int a = 0; a < m.getAllAtoms(); a++) {
            if (m.getAtomMapNo(a) == map1) {
                a1 = a;
                break;
            }
        }
        return a1;
    }

    int getMappedBondType(ExtendedMolecule m, int map1, int map2)
    {
        int ret = -1; // Bond Type
        int bond = getMappedBond(m, map1, map2);
        if (bond != -1) {
            return getBondType(m, bond);
        }
        return ret;
    }

    private int getBondType(ExtendedMolecule m, int bond)
    {
        int ret = m.getBondType(bond);
        if (m.isAromaticBond(bond) || m.isDelocalizedBond(bond)) {
            ret = Molecule.cBondTypeDelocalized;
        }
        return getSimpleBondType(ret);
    }

    private int getSimpleBondType(int bondtype)
    {
        if (bondtype == Molecule.cBondTypeDelocalized) {
            return Molecule.cBondTypeDelocalized;
        } else if ((bondtype & Molecule.cBondTypeSingle) == Molecule.cBondTypeSingle) {
            return Molecule.cBondTypeSingle;
        } else if ((bondtype & Molecule.cBondTypeDouble) == Molecule.cBondTypeDouble) {
            return Molecule.cBondTypeDouble;
        } else if ((bondtype & Molecule.cBondTypeTriple) == Molecule.cBondTypeTriple) {
            return bondtype & Molecule.cBondTypeTriple;
        }
        return bondtype;
    }

    int getMappedBond(ExtendedMolecule m, int map1, int map2)
    {
        int ret = -1; // Bond
        int a1 = getMappedAtom(m, map1);
        if (a1 != -1) {
            int bcnt = m.getConnAtoms(a1);
            for (int b = 0; b < bcnt; b++) {
                int a2 = m.getConnAtom(a1, b);
//                int a2 = m.getBondAtom(a1,b);
                int mt = m.getAtomMapNo(a2);
                if (debug) {
                    System.out.println(String.format("Checking Product bond %d-%d : Product Atom %d has map no %d", a1, a2, a2, mt));
                }
                if (mt == map2) {
                    ret = m.getConnBond(a1, b);
                    break;
                }
            }
        }
        return ret;
    }

//    public int getKey(ExtendedMolecule s, int a1, int a2, int bond)
//    {
//        return 0;
//    }


    private int getSameBond(ExtendedMolecule source, int cBondAtom, int cBondIndex, ExtendedMolecule target, int targetAtom)
    {
        int cAtomSymbol = source.getAtomicNo(cBondAtom);
        int map = source.getAtomMapNo(cBondAtom);
//        int cBondAtom = source.getConnAtom(sourceAtom,cbi);
//		int cBondIndex = source.getConnBond(sourceAtom,cbi);
        int cBondType = getBondType(source, cBondIndex);
        int targetBondCount = target.getConnAtoms(targetAtom);
        for (int tbi = 0; tbi < targetBondCount; tbi++) {
            int tBondAtom = target.getConnAtom(targetAtom, tbi);
            int tBondIndex = target.getConnBond(targetAtom, tbi);
            int tBondType = getBondType(target, tBondIndex);
            int tAtomSymbol = target.getAtomicNo(tBondAtom);
            int tmap = target.getAtomMapNo(tBondAtom);
            if (cAtomSymbol == tAtomSymbol && cBondType == tBondType && map == tmap) {
                return tBondIndex;
            }
        }
        return -1;
    }

    private int getBond(ExtendedMolecule mol, int atom1, int atom2)
    {
        int ret = -1;
        int bondCount = mol.getConnAtoms(atom1);
        for (int tbi = 0; tbi < bondCount; tbi++) {
            int tBondAtom = mol.getConnAtom(atom1, tbi);
            if (tBondAtom == atom2) {
                ret = mol.getConnBond(atom1, tbi);
                break;
            }
        }
        return ret;
    }

    private int findMapAt(ExtendedMolecule mol, int atom, int map)
    {
        int ret = -1;
        int bondCount = mol.getConnAtoms(atom);
        int m = mol.getAtomMapNo(atom);
        if (m == map) {
            ret = atom;
        }
        if (ret == -1) {
            for (int tbi = 0; tbi < bondCount; tbi++) {
                int tBondAtom = mol.getConnAtom(atom, tbi);
                m = mol.getAtomMapNo(tBondAtom);
                if (m == map) {
                    ret = tBondAtom;
                    break;
                }
            }
        }
        return ret;
    }

    private void generateKeys(Reaction rxn)
    {

        // (1)
        // FIND ATOM WITH MAP NO IN TARGET
        // FOR ALL ATOMS ON TARGET
        // IF MAP(ATOM)== MAP
        // TATOM = ATOM
        // END FOR

        //(2)
        // for all bonds at targetatom
        // if map(atom)== map
        // return atom
        // end for

        StringBuilder sb = new StringBuilder();
        ExtendedMolecule m = new ExtendedMolecule();
        initKeys();
        if (debug) {
            System.out.println(" " + m.toString());
        }
        int comp = rxn.getReactants();
        int prod = rxn.getProducts();
        int flags = Molecule.cHelperBitNeighbours | Molecule.cHelperBitRings;

        if (comp != 0 && prod != 0) {
            if (debug) {
                System.out.println("RXNSearcher.search()");
            }
            ExtendedMolecule component = new StereoMolecule(rxn.getMolecule(0));
            for (int c = 1; c < comp; c++) {
                ExtendedMolecule mol = rxn.getMolecule(c);
                component.addMolecule(mol);
            }
            component.ensureHelperArrays(flags);
            //sb.append(String.format("Component has %d atoms\n",component.getAllAtoms()));

            ExtendedMolecule product = new StereoMolecule(rxn.getProduct(0));
            for (int c = 1; c < prod; c++) {
                product.addMolecule(rxn.getProduct(c));
            }
            product.ensureHelperArrays(flags);

            ExtendedMolecule source = component;
            ExtendedMolecule target = product;

            // All Mapped atom indizes
            int mappedSourceAtoms[] = new int[source.getAllAtoms()];
            int mappedTargetAtoms[] = new int[target.getAllAtoms()];
            boolean cVisitedBonds[] = new boolean[source.getAllBonds()];

            //sb.append(String.format("source has %d atoms\n",source.getAllAtoms()));
            int noMappedSourceAtoms = 0;
            for (int a = 0; a < source.getAllAtoms(); a++) {
                int map = source.getAtomMapNo(a);
                if (map > 0) {
                    mappedSourceAtoms[noMappedSourceAtoms++] = a;
                }

            }


            int noMappedTargetAtoms = 0;
            for (int a = 0; a < target.getAllAtoms(); a++) {
                int map = target.getAtomMapNo(a);
                if (map > 0) {
                    mappedTargetAtoms[noMappedTargetAtoms++] = a;
                }

            }

            // setup SOURCEBOND and TARGETBOND ARRAY based on MAPPED ATOMS
            boolean sourceBonds[] = new boolean[source.getAllBonds()];
            boolean targetBonds[] = new boolean[target.getAllBonds()];


//            for (int k = 0; k < idx; k++) {
////                System.out.printf("Map [%d] -> [%d]\n",k,source.getAtomMapNo(k));
//                int s = source.getConnAtoms(k);
//                for (int b = 0; b < s; b++) {
//                    int a2 = source.getConnAtom(k, b);
//                    System.out.printf("Map #[%d] -> M[%d] = #[%d] -> M[%d]\n",
//                        k,source.getAtomMapNo(k),a2,
//                        source.getAtomMapNo(a2));
//                }
//            }
//            if (true)
//                return "";

            // for all mapped atoms
            for (int k = 0; k < noMappedSourceAtoms; k++) {
                // sourceAtom
                int sourceAtom = mappedSourceAtoms[k];
                int sourceMapNo = source.getAtomMapNo(sourceAtom);
                // targetAtom = find atom with same map no as sourceAtom (1)
                int targetAtom = getMappedAtom(target, sourceMapNo);
                debug("Working on atom %d (%d) target atom is %d\n", sourceMapNo, sourceAtom, targetAtom);

                // if not found
                // break;
                if (targetAtom == -1) {
                    break;
                }

//                debug("Map first (%d) (%d-%d)\n",sourceMapNo, sourceAtom,targetAtom);

                // boundCounts at source and target atom
                int sourceBondCount = source.getConnAtoms(sourceAtom);
                int targetBondCount = target.getConnAtoms(targetAtom);
                // for all bonds at source Atom
                for (int cbi = 0; cbi < sourceBondCount; cbi++) {
                    int sourceBondConnectedAtom = source.getConnAtom(sourceAtom, cbi);
                    int sourceBondIndex = source.getConnBond(sourceAtom, cbi);
                    int sourceBondType = getBondType(source, sourceBondIndex);
                    // if sourceBond is not visited
                    if (!sourceBonds[sourceBondIndex]) {
                        // amap = map(bondAtom)
                        int sourceBondConnectedAtomMap = source.getAtomMapNo(sourceBondConnectedAtom);
                        debug("\tChecking Source Bond on atom %d (%d)\n",
                            sourceBondConnectedAtomMap, sourceBondConnectedAtom);

                        // if bondAtom is mapped
                        if (sourceBondConnectedAtomMap != 0) {
                            // find atom with amap as at targetAtom bonds
                            int targetBondAtom = findMapAt(target, targetAtom, sourceBondConnectedAtomMap);
//                            debug("Mapping source bond %d (%d-%d) (%d-%d)\n",
//                                cbi,
//                                sourceMapNo,
//                                sourceBondConnectedAtomMap,
//                                sourceAtom,
//                                sourceBondConnectedAtom
//                            );
                            // if same map is found
                            if (targetBondAtom != -1) {
                                int targetBondIndex = getBond(target, targetAtom, targetBondAtom);
                                // TARGETBOND is visited
                                targetBonds[targetBondIndex] = true;
                                // if bondtype differs
                                int targetBondType = getBondType(target, targetBondIndex);
                                // The bond types don't match
                                if (targetBondType != sourceBondType) {
                                    // its a change
                                    // sourceBond[i] is visited
                                    if (debug) {
                                        System.out.println(String.format("Map: %d-%d bond CHANGE %d/%d", sourceMapNo, sourceBondConnectedAtomMap, sourceBondType, targetBondType));
                                    }
                                    sourceBonds[sourceBondIndex] = true;
                                    addChangeKey(source.getAtomicNo(sourceAtom), source.getAtomicNo(sourceBondConnectedAtom), sourceBondType, targetBondType);
                                    //addKey(key);
                                    //addKey(CH_ONLY);
                                    // else
                                } else {
                                    // sourceBond[i] is visited
                                    sourceBonds[sourceBondIndex] = true;
                                    // no change
                                    // end if
                                }
                                // else (map not found)
                            } else {
                                // its a break;
                                // sourceBond[i] is visited
                                if (debug) {
                                    System.out.println(String.format("Map: %d-%d bond BREAK %d", sourceMapNo, sourceBondConnectedAtomMap, sourceBondType));
                                }
                                sourceBonds[sourceBondIndex] = true;
                                addDeleteKey(sourceBondType, source.getAtomicNo(sourceAtom), source.getAtomicNo(sourceBondConnectedAtom));
//                                addKey(key);
//                                addKey(DEL_ONLY);
                                // ??? don't mark bond as visited we want to find the CREATE
                                // end if
                            }
                        } else {
                            ;
                            // Not a mapped atom, we will deal with this in the next section
                            // System.out.println(String.format("Map: %d Not mapped atom", sourceMapNo));
                            // not mapped atom
                            // ignore
                        }
                    } // end if
                } // end for

                // Deal with the non-mapped atoms...
                // for all bonds at source Atom
                for (int cbi = 0; cbi < sourceBondCount; cbi++) {
                    int cBondAtom = source.getConnAtom(sourceAtom, cbi);
                    int cBondIndex = source.getConnBond(sourceAtom, cbi);
                    int cBondType = getBondType(source, cBondIndex);
//                    int cAtomSymbol = source.getAtomicNo(cBondAtom);
                    // if sourceBond is not visited
                    // System.out.println(String.format("Map %d running at atom %d map %d",sourceMapNo,cBondAtom,source.getAtomMapNo(cBondAtom)));
                    if (!sourceBonds[cBondIndex]) {
                        int cbMap = source.getAtomMapNo(cBondAtom);
                        // if bondAtom is not  mapped
                        if (cbMap == 0) {
                            int tBondIndex = getSameBond(source, cBondAtom, cBondIndex, target, targetAtom);
                            if (tBondIndex != -1) {
                                // sourcebond is visited
                                // TARGETBOND IS VISITED
                                sourceBonds[cBondIndex] = true;
                                targetBonds[tBondIndex] = true;
                                // System.out.println(String.format("No Map/No change -> found bond index %d ", sourceMapNo));
                                // no change
                                // else
                            } else {
                                // BREAK_BOND
                                // sourcebond is visited
//                                if (debug)
//                                    System.out.println(String.format("Map: %d (%d-%d) Atom Type: %d  bond at atom " +
//                                        "BREAK",
//                                        sourceMapNo,
//                                        sourceAtom,cBondAtom,
//                                        source.getAtomicNo(cBondAtom)));
                                sourceBonds[cBondIndex] = true;
                                addDeleteKey(cBondType, source.getAtomicNo(sourceAtom), source.getAtomicNo(cBondAtom));
                                if (debug) {
                                    System.out.println(String.format("Map: %d (%d-%d) Atom Type: %d  bond at atom " +
                                            "DELETE",
                                        sourceMapNo,
                                        sourceAtom, cBondAtom,
                                        source.getAtomicNo(cBondAtom)));
                                }

//                                addKey(key);
                                //addKey(DEL_ONLY);
                                // end if
                            }
                        } else {
                            // This should not happen, since we dealt with this already before
                            if (debug) {
                                System.out.println(String.format("Map: %d Found SHOULD NOT", cbMap));
                            }
                        } // end if
                    } else {
                        // The bond has already been
                        // else
                        //???
                    }
                    // end if
                    // end for
                }
                // for all target bounds which have not yet been visited
                // they might be created, however, we'd need to check whether there's a mapping
//                if (sourceBondCount < targetBondCount) {
                // for all bonds at target atom
                for (int tbi = 0; tbi < targetBondCount; tbi++) {
                    int tBondIndex = target.getConnBond(targetAtom, tbi);
                    int tBondType = getBondType(target, tBondIndex);
                    int tAtom = target.getConnAtom(targetAtom, tbi);
                    int tmap = target.getAtomMapNo(tAtom);
                    int an = target.getAtomicNo(tAtom);
                    // if targetbond is not visited
                    if (!targetBonds[tBondIndex]) {
                        //those are CREATE_BONDS
                        if (debug) {
                            System.out.println(String.format("Map: %d-%d (%d) bond at atom CREATE_2", sourceMapNo, tmap, an));
                        }
                        targetBonds[tBondIndex] = true;
                        int key = addCreateKey(tBondType, target.getAtomicNo(targetAtom), target.getAtomicNo(tAtom));
                        addKey(key);
                        addKey(CR_ONLY);

                    } //endif
                } // end for
//                } //endif
            } //end for all mapped atoms
        }
    }

    public void debug(String format, Object... args)
    {
        if (debug)
            System.out.printf(format, args);

    }

    public static void main(String args[])
    {
        ReactionIndexer rs = new ReactionIndexer();
        try {

            for (int i = 1; i <= 11; i++) {
                RXNFileParser p = new RXNFileParser();
                Reaction r = new Reaction();
                p.parse(r, new java.io.File("RXN" + i + ".rxn"));
                if (debug) {
                    System.out.println("RXN: " + r.getMolecules());
                }
                rs.getKeys(r);
            }
        } catch (Throwable e) {
            System.err.println("Error parsing RDFILE " + e);
            e.printStackTrace();
        }
    }


}


/*
class RDFileReader
{
    BufferedReader rd = null;
    StringBuilder rxn = null;
    StringBuilder data = null;

    public RDFileReader(InputStream is) throws IOException
    {
        rd = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
        String line;
        line = rd.readLine();
        if (line == null || !line.startsWith("$RDFILE "))
            throw new IOException("Invalid File Header");
        line = rd.readLine();
        if (line == null)
            throw new IOException("Invalid File Header");
        line = rd.readLine();
        if (line == null || !line.startsWith("$RFMT"))
            throw new IOException("File is empty");
    }

    boolean hasNext()
    {
        boolean ret = false;
        StringBuilder r = new StringBuilder();
        StringBuilder d = new StringBuilder();
        // The default buffer is the reaction buffer
        StringBuilder sb = r;
        String line;
        try {
            boolean eof = true;
            while ((line = rd.readLine()) != null) {
                eof = false;
                if (line.startsWith("$RFMT")) {
//                        System.out.println("End of This RXN..." +line);
                    break;
                }
                // Switch the buffer to the data buffer
                if (line.startsWith("$DTYPE")) {
//                        System.out.println("Switching To Data Buffer " + line);
                    sb = data;
                }
                sb.append(line);
                sb.append("\n");
            }
            if (!eof) {
                data = d;
                rxn = r;
                ret = true;
            }
        } catch (IOException e) {
            ret = false;
        }
        return ret;
    }

    public Reaction getReaction()
    {
        Reaction r = new Reaction();
        RXNFileParser p = new RXNFileParser();
        try {
            r = p.getReaction(rxn.toString());
        } catch (Exception e) {
            System.err.println("Error parsing reaction...");
            r = null;
        }
        return r;
    }

}
*/
