// XDrawChem
// Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
// Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

// molecule_tools_2.cpp - Molecule's implementation of (more) functions

#include "defs.h"
#include "dpoint.h"
#include "drawable.h"
#include "graphdialog.h"
#include "moldata.h"
#include "molecule.h"
#include "molinfodialog.h"
#include "render2d.h"
//#include "namer.h"

// Preferences
extern Preferences preferences;

// Scale the molecule.  Should be used when loading the file.
// bond_length sets the AVERAGE bond length.  bond_length < 0 means
// set according to current preferences.
void Molecule::Scale(double bond_length) {
    double bond_sum = 0.0, bond_avg = 0.0, sf = 1.0;
    int nbonds = 0;

    // calculate current average bond length
    for (Bond *tmp_bond : bonds) {
        bond_sum += tmp_bond->Length();
        nbonds++;
    }
    bond_avg = bond_sum / (double)nbonds;
    if (bond_length < 0.0) {
        bond_length = preferences.getBond_fixedlength();
    }
    sf = bond_length / bond_avg;

    double topedge = 9999.0, leftedge = 9999.0;

    up = AllPoints();
    for (DPoint *tmp_pt : up) {
        if (tmp_pt->x < leftedge)
            leftedge = tmp_pt->x;
        if (tmp_pt->y < topedge)
            topedge = tmp_pt->y;
    }
    for (DPoint *tmp_pt : up) {
        tmp_pt->x = ((tmp_pt->x - leftedge) * sf) + (leftedge * sf);
        tmp_pt->y = ((tmp_pt->y - topedge) * sf) + (topedge * sf);
    }
}

QList<DPoint *> Molecule::BreakRingBonds(DPoint *target1) {
    QList<DPoint *> bb;

    for (Bond *tmp_bond : bonds) {
        if (tmp_bond->Find(target1) == true) {
            DPoint *tmp_pt = tmp_bond->otherPoint(target1);
            tmp_pt->new_order = tmp_bond->Order();
            bb.append(tmp_pt);
            bonds.removeAll(tmp_bond);
            tmp_bond = bonds.first();
        }
    }

    return bb;
}

DPoint *Molecule::GetAttachPoint(QString sf) {
    DPoint *tmp_pt;
    if ((sf.contains("fmoc") > 0) || (sf.contains("boc") > 0) || (sf.contains("dabcyl") > 0) ||
        (sf.contains("dabsyl") > 0) || (sf.contains("dansyl") > 0)) {
        up = AllPoints();
        foreach (tmp_pt, up) {
            if (tmp_pt->element == "Cl") {
                qDebug() << "Point:Cl";
                tmp_pt->element = "C";
                break;
            }
        }
        for (Text *tmp_text : labels) {
            if (tmp_text->Start() == tmp_pt) {
                qDebug() << "removed";
                labels.removeAll(tmp_text);
                break;
            }
        }
        qDebug() << tmp_pt->element;
        return tmp_pt;
    }
    if ((sf.contains("edans") > 0)) {
        up = AllPoints();
        foreach (tmp_pt, up) {
            if (tmp_pt->element == "NH2") {
                qDebug() << "Point:NH2";
                tmp_pt->element = "C";
                break;
            }
        }
        for (Text *tmp_text : labels) {
            if (tmp_text->Start() == tmp_pt) {
                qDebug() << "removed";
                labels.removeAll(tmp_text);
                break;
            }
        }
        qDebug() << tmp_pt->element;
        return tmp_pt;
    }
    if ((sf.contains("biotin") > 0)) {
        up = AllPoints();
        foreach (tmp_pt, up) {
            if (tmp_pt->element == "OH") {
                qDebug() << "Biotin-Point:OH";
                tmp_pt->element = "C";
                break;
            }
        }
        for (Text *tmp_text : labels) {
            if (tmp_text->Start() == tmp_pt) {
                qDebug() << "removed";
                labels.removeAll(tmp_text);
                break;
            }
        }
        qDebug() << tmp_pt->element;
        return tmp_pt;
    }
    // end biotin

    // for amino acids
    double ymax = 0.0;
    DPoint *ymaxpt = 0;

    up = AllPoints();
    foreach (tmp_pt, up) {
        if (tmp_pt->element.contains("N") > 0) {
            qDebug() << "Point:NH2";
            if (tmp_pt->y > ymax) {
                ymaxpt = tmp_pt;
                ymax = tmp_pt->y;
            }
        }
    }
    ymaxpt->element = "C";
    for (Text *tmp_text : labels) {
        if (tmp_text->Start() == ymaxpt) {
            qDebug() << "removed";
            labels.removeAll(tmp_text);
            break;
        }
    }
    qDebug() << ymaxpt->element;
    return ymaxpt;
}

DPoint *Molecule::GetRingAttachPoint() {
    double ymin = 99999.0;
    DPoint *yminpt = 0;

    up = AllPoints();
    for (DPoint *tmp_pt : up) {
        if (tmp_pt->y < ymin) {
            yminpt = tmp_pt;
            ymin = tmp_pt->y;
        }
    }

    return yminpt;
}

double Molecule::CalculateRingAttachAngle(DPoint *t1) {
    double dx = 0.0, dy = 0.0;

    for (Bond *tmp_bond : bonds) {
        if (tmp_bond->Find(t1) == true) {
            DPoint *tmp_pt = tmp_bond->otherPoint(t1);
            dx = dx + (tmp_pt->x - t1->x);
            dy = dy + (tmp_pt->y - t1->y);
            break;
        }
    }

    double ang = atan(dy / dx);

    if (dx > 0.0)
        ang = ang + 3.14159;

    return ang;
}

// return sum of gas-phase bond enthalpy
double Molecule::SumBondEnthalpy() {
    double dh = 0.0;

    for (Bond *tmp_bond : bonds) {
        dh += tmp_bond->Enthalpy();
    }

    return dh;
}

// set group information
void Molecule::setGroupType(int g1) { group_type = g1; }

// build DPoint::neighbors list(s)
void Molecule::AllNeighbors() {
    int n1, n2, n3;

    QList<DPoint *> groupAtoms = AllPoints();

    for (DPoint *tmp_pt : groupAtoms) {
        n1 = 0;
        n2 = 0;
        n3 = 0;
        tmp_pt->neighbors.clear();
        tmp_pt->aromatic = false;
        tmp_pt->inring = false;
        for (Bond *tmp_bond : bonds) {
            if (tmp_bond->Find(tmp_pt) == true) {
                if (tmp_bond->Order() == 1)
                    n1++;
                if (tmp_bond->Order() == 5)
                    n1++;
                if (tmp_bond->Order() == 7)
                    n1++;
                if (tmp_bond->Order() == 2)
                    n2++;
                if (tmp_bond->Order() == 3)
                    n3++;
                tmp_pt->neighbors.append(tmp_bond->otherPoint(tmp_pt));
                tmp_pt->bondorder[(tmp_pt->neighbors.count() - 1)] = tmp_bond->Order();
            }
        }
        tmp_pt->hybrid = "unknown";
        if ((n1 == 1) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3";
        if ((n1 == 2) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3";
        if ((n1 == 3) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3";
        if ((n1 == 4) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3";
        if ((n1 == 5) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3d2";
        if ((n1 == 6) && (n2 == 0) && (n3 == 0))
            tmp_pt->hybrid = "sp3d2";
        if ((n1 == 0) && (n2 == 1) && (n3 == 0))
            tmp_pt->hybrid = "sp2";
        if ((n1 == 1) && (n2 == 1) && (n3 == 0))
            tmp_pt->hybrid = "sp2";
        if ((n1 == 2) && (n2 == 1) && (n3 == 0))
            tmp_pt->hybrid = "sp2";
        if ((n1 == 0) && (n2 == 2) && (n3 == 0))
            tmp_pt->hybrid = "sp";
        if ((n1 == 0) && (n2 == 0) && (n3 == 1))
            tmp_pt->hybrid = "sp";
        if ((n1 == 1) && (n2 == 0) && (n3 == 1))
            tmp_pt->hybrid = "sp";
        if ((n1 == 2) && (n2 == 2) && (n3 == 0))
            tmp_pt->hybrid = "spd2";
    }
}

// invoke from chemdata_tools.cpp
// revised: now invoked from reactivity functions in Molecule
// revised again:  moved to static function in MolData class
double Molecule::CalcPartialCharge(QString atomtype) { return 0.0; }

Text *Molecule::CalcEmpiricalFormula(bool from_mw) {
    QList<DPoint *> up;
    QString ef;
    QStringList allatoms, oddatoms;

    up = AllPoints();
    // Split all labels into allatoms (one atom per entry)
    for (DPoint *tmp_pt : up) {
        // parse this string
        QString x = tmp_pt->plainElement();
        QString iso;         // isotope MW
        QString thiselement; // current element
        QString repeatnum;   // number of repeats
        int ptr = 0;         // cursor position (pointer) in x
        int isoflag = false; // isotope-override normal MW lookup if MW specified
        int repeat = 1;      // number of this atom

        do {
            iso.remove(0, 999);
            thiselement.remove(0, 999);
            repeatnum.remove(0, 999);
            // Check if token starts with a number
            if (x.at(ptr).isNumber() == true) { // read isotope value
                isoflag = true;
                iso.append(x.at(ptr));
                ptr++;
                if (x.at(ptr).isNumber() == true) {
                    iso.append(x.at(ptr));
                    ptr++;
                }
                if (x.at(ptr).isNumber() == true) {
                    iso.append(x.at(ptr));
                    ptr++;
                }
            }
            // ptr now points to first letter of element
            thiselement.append(x.at(ptr));
            ptr++;
            qDebug() << x << " " << ptr;
            // if next letter is lowercase, add it too
            if (ptr < x.length() && x.at(ptr).category() == QChar::Letter_Lowercase) {
                thiselement.append(x.at(ptr));
                ptr++;
            }
            // if next character is number, it's the subscript
            if (ptr < x.length() && x.at(ptr).isNumber()) {
                repeatnum.append(x.at(ptr));
                ptr++;
                if (ptr < x.length() && x.at(ptr).isNumber()) {
                    repeatnum.append(x.at(ptr));
                    ptr++;
                }
                if (ptr < x.length() && x.at(ptr).isNumber()) {
                    repeatnum.append(x.at(ptr));
                    ptr++;
                }
                repeat = repeatnum.toInt();
            }
            // Move to next letter/number
            if (ptr < x.length() && x.at(ptr).isSpace()) {
                ptr++;
            }
            // add atoms to list
            for (int cc = 0; cc < repeat; cc++)
                allatoms.append(thiselement);
            isoflag = false;
            repeat = 1;
        } while (ptr < x.length());
    }
    // need to find implicit hydrogens here!
    int num_c = 0, num_h = 0, num_n = 0, num_o = 0, num_p = 0, num_s = 0;

    for (DPoint *tmp_pt : up) {
        int possible_h = 0;

        // qDebug() << "CalcEF:" << tmp_pt->element;
        possible_h = MolData::Hydrogens(tmp_pt->plainElement());
        for (Bond *tmp_bond : bonds) {
            if (tmp_bond->Find(tmp_pt))
                possible_h -= tmp_bond->Order();
        }
        if (possible_h < 0) {
            qDebug() << tmp_pt->element << " resulted in negative hydrogens ";
            qDebug() << "in Molecule::CalcEmpiricalFormula()";
            possible_h = 0;
        }
        num_h += possible_h;
    }

    // convert allatoms to formula: first extract C,H,O,N,P,S
    // calculate molecular weight too
    nmw = (double)num_h * 1.00794;

    for (QStringList::Iterator it = allatoms.begin(); it != allatoms.end(); ++it) {
        nmw += MolData::NameToMW(*it);
        if (*it == "C") {
            num_c++;
            continue;
        }
        if (*it == "H") {
            num_h++;
            continue;
        }
        if (*it == "O") {
            num_o++;
            continue;
        }
        if (*it == "N") {
            num_n++;
            continue;
        }
        if (*it == "P") {
            num_p++;
            continue;
        }
        if (*it == "S") {
            num_s++;
            continue;
        }
        oddatoms.append(*it);
    }

    // qDebug() << "Found " << oddatoms.count() << " odd atoms." ;
    // sort odd atom list alphabetically (if necessary)
    if (oddatoms.count() > 0)
        oddatoms.sort();

    QString finalef, n1;

    if (num_c > 0) {
        n1.setNum(num_c);
        finalef = finalef + "C";
        if (num_c > 1)
            finalef = finalef + n1;
    }
    if (num_h > 0) {
        n1.setNum(num_h);
        finalef = finalef + "H";
        if (num_h > 1)
            finalef = finalef + n1;
    }
    if (num_o > 0) {
        n1.setNum(num_o);
        finalef = finalef + "O";
        if (num_o > 1)
            finalef = finalef + n1;
    }
    if (num_n > 0) {
        n1.setNum(num_n);
        finalef = finalef + "N";
        if (num_n > 1)
            finalef = finalef + n1;
    }
    if (num_p > 0) {
        n1.setNum(num_p);
        finalef = finalef + "P";
        if (num_p > 1)
            finalef = finalef + n1;
    }
    if (num_s > 0) {
        n1.setNum(num_s);
        finalef = finalef + "S";
        if (num_s > 1)
            finalef = finalef + n1;
    }
    // add odd atoms
    if (oddatoms.count() > 0) {
        do {
            QString te = oddatoms.first();
            int tc = 0;

            for (QStringList::Iterator ir = oddatoms.begin(); ir != oddatoms.end(); ++ir) {
                if (*ir == te)
                    tc++;
            }
            finalef = finalef + te;
            n1.setNum(tc);
            if (tc > 1)
                finalef = finalef + n1;
            oddatoms.removeAll(te);
        } while (oddatoms.count() > 0);
    }
    // these are used for elemental analysis, don't delete
    nc = num_c;
    nh = num_h;
    nn = num_n;
    no = num_o;

    double nx, ny;

    QRect nr = BoundingBoxAll();

    ny = nr.top() - 16.0;
    nx = (nr.left() + nr.right()) / 2.0;

    Text *tmp_text = new Text(r);
    DPoint *tmp_pt = new DPoint(nx, ny);
    tmp_text->setPoint(tmp_pt);
    tmp_text->setJustify(JUSTIFY_TOPLEFT);
    tmp_text->setText(finalef);
    for (int c = 0; c < finalef.length(); c++) {
        if (finalef[c].isLetter())
            finalef.replace(c, 1, " ");
        if (finalef[c].isNumber())
            finalef.replace(c, 1, "-");
    }
    //    tmp_text->setTextMask( finalef );

    // if (from_mw == false) {
    //  text_formula = tmp_text;
    //  tmp_text->setMolecule(this);
    //  tmp_text->setDataType(TEXT_DATA_FORMULA);
    //}

    return tmp_text;
}

Text *Molecule::CalcMW(bool from_change) {
    Text *tmp_txt1;

    tmp_txt1 = CalcEmpiricalFormula(true);

    double nx, ny;

    QRect nr = BoundingBoxAll();

    ny = nr.bottom() + 5.0;
    nx = (nr.left() + nr.right()) / 2.0;

    Text *tmp_text = new Text(r);
    DPoint *tmp_pt = new DPoint(nx, ny);
    tmp_text->setPoint(tmp_pt);
    tmp_text->setJustify(JUSTIFY_TOPLEFT);
    QString finalmw;

    finalmw.setNum(nmw);
    finalmw = QString("MW = ") + finalmw;
    tmp_text->setText(finalmw);
    finalmw.fill(' ');
    //    tmp_text->setTextMask( finalmw );

    // if (from_change == false) {
    //  text_mw = tmp_text;
    //  tmp_text->setMolecule(this);
    //  tmp_text->setDataType(TEXT_DATA_MW);
    //}

    return tmp_text;
}

Text *Molecule::CalcElementalAnalysis(bool show_dialog) {
    QString ea, n1;

    // calc empirical formula and total molecular weight
    Text *tmp_text = CalcMW();
    nc *= 12.011;
    nh *= 1.00794;
    nn *= 14.0067;
    no *= 15.994;
    nc = nc * 100 / nmw;
    nh = nh * 100 / nmw;
    no = no * 100 / nmw;
    nn = nn * 100 / nmw;

    ea.append(tr("Elemental analysis:\n"));
    ea.append("C = ");
    n1.setNum(nc);
    ea.append(n1);
    ea.append("%\n");
    ea.append("H = ");
    n1.setNum(nh);
    ea.append(n1);
    ea.append("%\n");
    ea.append("O = ");
    n1.setNum(no);
    ea.append(n1);
    ea.append("%\n");
    ea.append("N = ");
    n1.setNum(nn);
    ea.append(n1);
    ea.append("%");

    /*
       int m1;
       if (show_dialog == true) {
       m1 = QMessageBox::information(r, tr("Elemental analysis"), ea,
       tr("Paste"), tr("OK"), QString::null, 1, 1); } else { m1 = 0;
       }
     */

    double nx, ny;

    QRect nr = BoundingBoxAll();

    ny = nr.top() + 8.0;
    nx = nr.right() + 16.0;

    tmp_text = new Text(r);
    // CHECK_PTR(tmp_text);
    DPoint *tmp_pt = new DPoint(nx, ny);
    tmp_text->setPoint(tmp_pt);
    // qDebug() << ea;
    tmp_text->setJustify(JUSTIFY_TOPLEFT);
    tmp_text->setText(ea);
    ea.fill(' ');
    //    tmp_text->setTextMask( ea );
    return tmp_text;
}

void Molecule::AddPeak(double d1, QString s1, QString s2) {
    Peak *tmp_peak = new Peak;
    tmp_peak->value = d1;
    tmp_peak->multiplicity = 1;
    tmp_peak->intensity = 80;
    if (s2.contains("broad") > 0)
        tmp_peak->intensity = 40;
    if (s2.contains("narrow") > 0)
        tmp_peak->intensity = 120;
    tmp_peak->comment = s2;
    foreach (Peak *tpeak, peaklist) {
        if ((tpeak->value == tmp_peak->value) && (tpeak->multiplicity == tmp_peak->multiplicity)) {
            tpeak->intensity += 1;
            delete tmp_peak;

            tmp_peak = 0;
            break;
        }
    }
    if (tmp_peak != 0)
        peaklist.append(tmp_peak);
}

// add hydrogens to atoms.  Only add to carbon if to_carbons == true
void Molecule::AddHydrogens(bool to_carbon) {
    // qDebug() << "AddHydrogens(" << to_carbon << "), prefs = " <<
    // preferences.getFixHydrogens();
    up = AllPoints();
    int h = 0, sumbonds = 0;
    double dx;
    QString hnum, orig_element;
    int least_hindered_side;

    // calculate hindrance first
    for (DPoint *tmp_pt : up) {
        // find order of bonds
        least_hindered_side = 0;
        sumbonds = 0;
        for (Bond *tmp_bond : bonds) {
            if (tmp_bond->Find(tmp_pt)) {
                dx = tmp_pt->x - tmp_bond->otherPoint(tmp_pt)->x;
                if (dx > 0.5)
                    least_hindered_side++;
                if (dx < -0.5)
                    least_hindered_side--;
            }
        }
        // save # of bonds found
        // qDebug() << tmp_pt->toQPoint() << "(" << tmp_pt->element << "): found
        // substituents = " << sumbonds;
        tmp_pt->substituents = sumbonds;
        tmp_pt->C13_shift = least_hindered_side;
        // update Text, if it exists, with least hindered side
        for (Text *tmp_text : labels) {
            if (tmp_text->Start() == tmp_pt) {
                if (least_hindered_side < 0)
                    tmp_text->CheckAlignment(2); // left hindered
                else
                    tmp_text->CheckAlignment(1); // right hindered
            }
        }
    }
    // add hydrogens if user requested
    if (preferences.getFixHydrogens() == false)
        return;
    for (DPoint *tmp_pt : up) {
        orig_element = tmp_pt->element;
        if (tmp_pt->element == "")
            tmp_pt->element = "C";
        sumbonds = 0;
        least_hindered_side = 0;
        h = 0;
        // don't add to carbons unless specifically instructed
        if ((tmp_pt->element == "C") && (to_carbon == false))
            continue;
        // skip special cases
        if (tmp_pt->element == "CO")
            continue;
        if (tmp_pt->element == "SO")
            continue;
        // don't do fragments with charges
        if (tmp_pt->element.contains("+") > 0)
            continue;
        if (tmp_pt->element.contains("-") > 0)
            continue;
        // N typically needs correcting...
        if (tmp_pt->element == "HN")
            tmp_pt->element = "N";
        if (tmp_pt->element == "NH")
            tmp_pt->element = "N";
        if (tmp_pt->element == "H2N")
            tmp_pt->element = "N";
        if (tmp_pt->element == "NH2")
            tmp_pt->element = "N";
        if (tmp_pt->element == "H3N")
            tmp_pt->element = "N";
        if (tmp_pt->element == "NH3")
            tmp_pt->element = "N";
        // so does O
        if (tmp_pt->element == "HO")
            tmp_pt->element = "O";
        if (tmp_pt->element == "OH")
            tmp_pt->element = "O";
        if (tmp_pt->element == "H2O")
            tmp_pt->element = "O";
        if (tmp_pt->element == "OH2")
            tmp_pt->element = "O";
        // let's do thiols too, so H ends up on proper side
        if (tmp_pt->element == "HS")
            tmp_pt->element = "S";
        if (tmp_pt->element == "SH")
            tmp_pt->element = "S";
        // retrieve # of bonds found
        sumbonds = 0; // tmp_pt->substituents;
        for (Bond *tmp_bond : bonds) {
            if (tmp_bond->Find(tmp_pt)) {
                if (tmp_bond->Order() < 4)
                    sumbonds += tmp_bond->Order();
                if (tmp_bond->Order() >= 4)
                    sumbonds += 1;
            }
        }
        least_hindered_side = (int)tmp_pt->C13_shift;
        // don't add if hydrogen already present
        if (tmp_pt->element.contains("H") == 0) {
            h = MolData::Hydrogens(tmp_pt->element) - sumbonds;
            qDebug() << "Found hydrogens: " << h;
            if (h > 1)
                hnum.setNum(h);
            else
                hnum = "";
            if (h > 0) {
                if (least_hindered_side < 0)
                    tmp_pt->element.prepend("H" + hnum);
                else
                    tmp_pt->element.append("H" + hnum);
            }
        }
        // now find Text which reference this DPoint and copy back
        QString elbackup;

        if (orig_element != tmp_pt->element) {
            for (Text *tmp_text : labels) {
                if (tmp_text->Start() == tmp_pt) {
                    elbackup = tmp_pt->element;
                    qDebug() << tmp_pt->element;
                    tmp_text->setText(tmp_pt->element);
                    // *sigh* I suppose the least I can do is compute a proper
                    // textmask
                    elbackup.fill(' ');
                    if (h > 1) {
                        if (least_hindered_side < 0) {
                            elbackup.replace(1, 1, "-");
                        } else {
                            elbackup.replace(tmp_pt->element.length() - 1, 1, "-");
                        }
                    }
                    //                    tmp_text->setTextMask( elbackup );
                    // tmp_pt->element = elbackup;
                } // if (tmp_text->...)
            }     // for(tmp_text...)
        }         // if (orig...)
    }
}

// reactivity:  use this for doing partial charges/VdW for now
// int argument specifies analysis type
void Molecule::Reactivity(int react_type) {
    qDebug() << "Reactivity(" << react_type << ")";
    // get list of unique points
    up = AllPoints();
    // find aromatic rings
    MakeSSSR();
    // solve HOSE codes
    Calc13CNMR(false);
    // add hydrogens
    qDebug() << bonds.count();
    AddNMRprotons();
    up = AllPoints(); // need to re-run after adding protons
    qDebug() << bonds.count();

    // reactivity... build list of all atom and bond retro-types
    QString localatomtype;
    double sumcharge = 0.0;

    atomRxns.clear();
    bondRxns.clear();
    foreach (DPoint *tmp_atomrxniter, up) {
        localatomtype = RetroAtomName(tmp_atomrxniter);
        atomRxns.append(localatomtype);
        // set partial charges here
        tmp_atomrxniter->react_atom_type = localatomtype;
        tmp_atomrxniter->partial_charge = CalcPartialCharge(localatomtype);
        sumcharge += MolData::partialCharge(localatomtype, 0);
    }
    qDebug() << "Calc total charge: " << sumcharge;
    qDebug() << "Atom count: " << atomRxns.count();
    foreach (Bond *tmp_bondRxns, bonds) {
        if (tmp_bondRxns->isCHBond() == false) {
            bondRxns.append(RetroBondName(tmp_bondRxns));
            tmp_bondRxns->setCName(RetroBondName(tmp_bondRxns));
            // bondrxniter.current()->setReactions(BuildReactionList(bondrxniter.current()->CName()));
        }
    }
    qDebug() << "Bond count: " << bondRxns.count();

    // do stuff, based on value passed in arg
    if (react_type == MODE_TOOL_REACTIVITY_FORWARD) {
        // for reactivity, we should look at atom types first, since atoms will
        // be the likely target of forward reactions.
        qDebug() << "Atom type list for FORWARD:";
        for (QStringList::Iterator it = atomRxns.begin(); it != atomRxns.end(); ++it) {
            qDebug() << *it;
        }
        qDebug() << "--- list ends ---";
        // we will also have to consider other atom types and bonds that may
        // interfere with a given reaction
    }
    if (react_type == MODE_TOOL_REACTIVITY_RETRO) {
        // for retrosynthesis, we are more interested in bonds, to se how they
        // might have been formed
        qDebug() << "Bond type list for RETRO:";
        for (QStringList::Iterator it = bondRxns.begin(); it != bondRxns.end(); ++it) {
            qDebug() << *it;
        }
        qDebug() << "--- list ends ---";
        // we will also have to consider R group substitution here.
        // call Molecule::Retro() (in retro.cpp) to do matching
        /*int nr = */ Retro();
    }
    if (react_type == MODE_TOOL_CHARGES) {
        // just look at atom types and assign AMBER-like partial charges
        qDebug() << "Sum of charges = " << sumcharge;
    }
    // remove added hydrogens
    RemoveNMRprotons();
    qDebug() << bonds.count();
}
