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

#include <qobject.h>
#include <qlist.h>
#include <qstring.h>
#include <qmessagebox.h>
#include <iostream>
#include <math.h>

using std::cout;
using std::endl;

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

// Preferences
extern Preferences preferences;
// Prediction stuff
#include "boshcp.h"
// SSSR (see comments in this file)
#include "molecule_sssr.h"

// 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 (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next() ) {
    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 (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
    if (tmp_pt->x < leftedge) leftedge = tmp_pt->x;
    if (tmp_pt->y < topedge) topedge = tmp_pt->y;
  }
  for (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
    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 (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next() ) {
    if (tmp_bond->Find(target1) == true) {
      tmp_pt = tmp_bond->otherPoint(target1);
      tmp_pt->new_order = tmp_bond->Order();
      bb.append(tmp_pt);
      bonds.remove(tmp_bond);
      tmp_bond = bonds.first();
    }
  }

  return bb;
}

DPoint *Molecule::GetAttachPoint(QString sf) {
  if ( (sf.contains("fmoc") > 0) || 
       (sf.contains("boc") > 0) || 
       (sf.contains("dabcyl") > 0) || 
       (sf.contains("dabsyl") > 0) || 
       (sf.contains("dansyl") > 0) ) {
    up = AllPoints();
    for (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
      if (tmp_pt->element == "Cl") { 
	cout << "Point:Cl" << endl; 
	tmp_pt->element = "C";
	break;
      }
    }
    for (tmp_text = labels.first(); tmp_text != 0; tmp_text = labels.next()){
      if (tmp_text->Start() == tmp_pt) {
	cout << "removed" << endl;
	labels.remove(tmp_text);
	break;
      }
    }
    cout << tmp_pt->element << endl;
    return tmp_pt;
  }
  if ( (sf.contains("edans") > 0) ) {
    up = AllPoints();
    for (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
      if (tmp_pt->element == "NH2") { 
	cout << "Point:NH2" << endl; 
	tmp_pt->element = "C";
	break;
      }
    }
    for (tmp_text = labels.first(); tmp_text != 0; tmp_text = labels.next()){
      if (tmp_text->Start() == tmp_pt) {
	cout << "removed" << endl;
	labels.remove(tmp_text);
	break;
      }
    }
    cout << tmp_pt->element << endl;
    return tmp_pt;
  }

  // for amino acids
  double ymax = 0.0;
  DPoint *ymaxpt = 0; 
  up = AllPoints();
  for (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
    if (tmp_pt->element.contains("N") > 0) { 
      cout << "Point:NH2" << endl; 
      if (tmp_pt->y > ymax) {
	ymaxpt = tmp_pt;
	ymax = tmp_pt->y;
      }
    }
  }
  ymaxpt->element = "C";
  for (tmp_text = labels.first(); tmp_text != 0; tmp_text = labels.next()){
    if (tmp_text->Start() == ymaxpt) {
      cout << "removed" << endl;
      labels.remove(tmp_text);
      break;
    }
  }
  cout << ymaxpt->element << endl;
  return ymaxpt;
}

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

  up = AllPoints();
  for (tmp_pt = up.first(); tmp_pt != 0; tmp_pt = up.next() ) {
    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 (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next() ) {
    if (tmp_bond->Find(t1) == true) {
      tmp_pt = tmp_bond->otherPoint(t1);
      dx = dx + (tmp_pt->x - t1->x);
      dy = dy + (tmp_pt->y - t1->y);
    }
  }

  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(tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next() ) {
    dh += tmp_bond->Enthalpy();
  }

  return dh;
}

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

// note that this function builds the DPoint::neighbors list as well
void Molecule::MakeSSSR() {
  QList<DPoint> groupAtoms = AllPoints();

  for (tmp_pt = groupAtoms.first(); tmp_pt != 0; tmp_pt = groupAtoms.next() ) {
    tmp_pt->neighbors.clear();
    tmp_pt->aromatic = false;
    tmp_pt->inring = false;
    for(tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next() ) {
      if (tmp_bond->Find(tmp_pt) == true) {
	tmp_pt->neighbors.append(tmp_bond->otherPoint(tmp_pt));
	tmp_pt->bondorder.at(tmp_pt->neighbors.count() - 1) = tmp_bond->Order();
      }
    }
  }
  this_sssr.BuildSSSR(groupAtoms);
  this_sssr.PrintSSSR();
  this_sssr.FindAromatic(bonds);
}

void Molecule::CalcName() {
  Namer n1;
  QString nom = n1.GetName(AllPoints(), bonds);
  cout << nom << endl;
}
QString Molecule::IUPAC_Name() {
  return "Spam";
}

QStringList Molecule::Calc13CNMR(bool show_dialog) {
  BremserOneSphereHOSECodePredictor boshcp;
  QStringList hosecodes;
  QList<DPoint> up;
  QList<DPoint> sphere1;
  QString tmp_str, ts;
  DPoint *tmp_pt2, *tmp_pt3;
  int hs, hsmax;

  // get list of unique points
  up = AllPoints();
  // find rings (esp. find aromaticity) - do after CopyTextToDPoint()
  MakeSSSR();

  // Scan for keto groups
  for (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next()) {
    tmp_pt->ketos = 0;
    for (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next()) {
      if (tmp_bond->Find(tmp_pt) == true) {
	tmp_pt2 = tmp_bond->otherPoint(tmp_pt);
	if ( (tmp_pt2->element == "O") && (tmp_bond->Order() == 2) )
	  tmp_pt->ketos += 1;
      }
    }
  }
  // Determine atoms surrounding each atom and build HOSE code list
  for (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next()) {
    if (tmp_pt->element == "C") {  // only look at carbons
      for (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next()) {
	if (tmp_bond->Find(tmp_pt) == true) {
	  tmp_str = "";
	  hs = 0;
	  tmp_pt2 = tmp_bond->otherPoint(tmp_pt);
	  if (tmp_bond->Order() == 3) { tmp_str.append("%"); hs += 300; }
	  if ( (tmp_pt->aromatic == true) && (tmp_pt2->aromatic == true) ) {
	    tmp_str.append("*"); hs += 100;
	  } else {
	    if (tmp_bond->Order() == 2) {
	      if (tmp_bond->Dash() == 0) 
		{ tmp_str.append("="); hs += 200; }
	      if (tmp_bond->Dash() == 1)
		{ tmp_str.append("*"); hs += 100; }
	    }
	  }
	  if (tmp_pt2->element == "C") { tmp_str.append("C"); hs += 24; }
	  if (tmp_pt2->element == "O") { tmp_str.append("O"); hs += 22; }
	  if (tmp_pt2->element == "OH") { tmp_str.append("O"); hs += 22; }
	  if (tmp_pt2->element == "HO") { tmp_str.append("O"); hs += 22; }
	  if (tmp_pt2->element == "N") { tmp_str.append("N"); hs += 20; }
	  if (tmp_pt2->element == "NH2") { tmp_str.append("N"); hs += 20; }
	  if (tmp_pt2->element == "H2N") { tmp_str.append("N"); hs += 20; }
	  if (tmp_pt2->element == "NH") { tmp_str.append("N"); hs += 20; }
	  if (tmp_pt2->element == "HN") { tmp_str.append("N"); hs += 20; }
	  if (tmp_pt2->element == "P") { tmp_str.append("P"); hs += 18; }
	  if (tmp_pt2->element == "S") { tmp_str.append("S"); hs += 16; }
	  if (tmp_pt2->element == "Si") { tmp_str.append("Q"); hs += 14; }
	  if (tmp_pt2->element == "B") { tmp_str.append("B"); hs += 12; }
	  if (tmp_pt2->element == "F") { tmp_str.append("F"); hs += 10; }
	  if (tmp_pt2->element == "Cl") { tmp_str.append("X"); hs += 8; }
	  if (tmp_pt2->element == "Br") { tmp_str.append("Y"); hs += 6; }
	  if (tmp_pt2->element == "I") { tmp_str.append("I"); hs += 4; }
	  if (tmp_pt2->ketos == 2) { tmp_str.append("$$"); }
	  if (tmp_pt2->ketos == 1) { tmp_str.append("$"); }
	  tmp_pt2->tmphose = tmp_str;
	  tmp_pt2->hosescore = hs;
	  sphere1.append(tmp_pt2);
	}
      }
      tmp_str = "";
      do {
	hsmax = -1;
	for (tmp_pt3 = sphere1.first(); tmp_pt3 != 0; 
	     tmp_pt3 = sphere1.next()) {
	  if (tmp_pt3->hosescore > hsmax) { 
	    hsmax = tmp_pt3->hosescore; 
	    ts = tmp_pt3->tmphose;
	    tmp_pt2 = tmp_pt3;
	  }
	}
	sphere1.remove(tmp_pt2);
	tmp_str.append(ts);
      } while (sphere1.count() > 0);
      tmp_str.append("(//)");
      hosecodes.append(tmp_str);
      cout << tmp_str << endl;
    }
  }

  if (show_dialog == false) return hosecodes;

  GraphDialog *g = new GraphDialog(r, "Predicted 13C-NMR");
  // print (for now) list of NMR values
  for ( QStringList::Iterator it = hosecodes.begin(); 
	it != hosecodes.end(); ++it ) {
    //cout << boshcp.predictFull(*it) << endl;
    g->AddPeak(boshcp.predict(*it), boshcp.getMult(*it), 
	       boshcp.predictFull(*it));
  }

  QPixmap mol = r->MakeFullPixmap();
  QRect rr1 = BoundingBoxAll();
  rr1.setLeft(rr1.left() - 4);
  rr1.setTop(rr1.top() - 4);
  rr1.setBottom(rr1.bottom() + 4);
  rr1.setRight(rr1.right() + 4);
  QPixmap mol1(rr1.size());
  bitBlt(&mol1, QPoint(0,0), &mol, rr1);

  g->AddPixmap(mol1);
  g->show();

  return hosecodes;
}

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 (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next() ) {
    // parse this string
    QString x = tmp_pt->element;
    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++;
      // if next letter is lowercase, add it too
      if (x.at(ptr).category() == QChar::Letter_Lowercase) {
	thiselement.append(x.at(ptr));
	ptr++;
      }
      // if next character is number, it's the subscript
      if (x.at(ptr).isNumber() == true) {
	repeatnum.append(x.at(ptr));
	ptr++;
	if (x.at(ptr).isNumber() == true) {
	  repeatnum.append(x.at(ptr));
	  ptr++;
	}
	if (x.at(ptr).isNumber() == true) {
	  repeatnum.append(x.at(ptr));
	  ptr++;
	}
	repeat = repeatnum.toInt();
      }
      // Move to next letter/number
      if (ptr < x.length()) {
	if (x.at(ptr).isSpace() == true) 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 (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next() ) {
    int possible_h = 0;
    possible_h = MolData::Hydrogens(tmp_pt->element);
    for (tmp_bond = bonds.first(); tmp_bond != 0; tmp_bond = bonds.next()) {
      if (tmp_bond->Find(tmp_pt))
	possible_h -= tmp_bond->Order();
    }
    if (possible_h < 0) {
      cout << tmp_pt->element << " resulted in negative hydrogens ";
      cout << "in Molecule::CalcEmpiricalFormula()" << endl;
      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);
  }

  cout << "Found " << oddatoms.count() << " odd atoms." << endl;
  // 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.remove(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;

  tmp_text = new Text(r);
  //CHECK_PTR(tmp_text);
  //cout << finalef << endl;
  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);
  tmp_pt = new DPoint(nx, ny);
  tmp_text->setPoint(tmp_pt);

  //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_text;

  tmp_txt1 = CalcEmpiricalFormula(true);

  double nx, ny;

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

  tmp_text = new Text(r);
  tmp_text->setJustify(JUSTIFY_TOPLEFT);
  QString finalmw;
  finalmw.setNum(nmw);
  finalmw = i18n("MW = ") + finalmw;
  tmp_text->setText(finalmw);
  finalmw.fill(' ');
  tmp_text->setTextMask(finalmw);
  tmp_pt = new DPoint(nx, ny);
  tmp_text->setPoint(tmp_pt);

  //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
  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(i18n( "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, i18n( "Elemental analysis" ), ea, i18n( "Paste" ), "OK",
				  QString::null, 1, 1);
  } else {
    m1 = 0;
  }
  if (m1 == 0) {
    cout << "paste" << endl;
    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);
    //cout << finalef << endl;
    tmp_text->setJustify(JUSTIFY_TOPLEFT);
    tmp_text->setText(ea);
    ea.fill(' ');
    tmp_text->setTextMask(ea);
    tmp_pt = new DPoint(nx, ny);
    tmp_text->setPoint(tmp_pt);
    return tmp_text;
  }
  return 0;
}

// add hydrogens to atoms.  Only add to carbon if to_carbons == true
void Molecule::AddHydrogens(bool to_carbon) {
  up = AllPoints();
  int h, sumbonds;
  double dx;
  QString hnum, orig_element;
  int least_hindered_side;
  for (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next()) {
    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";
    // so does O
    if (tmp_pt->element == "HO") tmp_pt->element = "O";
    if (tmp_pt->element == "OH") 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";
    // find order of bonds
    for (tmp_bond = bonds.first(); tmp_bond!=NULL; tmp_bond = bonds.next()) {
      if (tmp_bond->Find(tmp_pt)) {
	if (tmp_bond->Order() < 4)
	  sumbonds += tmp_bond->Order();
	else
	  sumbonds += 1; // order 5 and 7 (stereo) and order 1 bonds
	dx = tmp_pt->x - tmp_bond->otherPoint(tmp_pt)->x;
	if (dx > 0.5)
	  least_hindered_side++;
	if (dx < -0.5)
	  least_hindered_side--;
      }
    }
    // update Text, if it exists, with least hindered side
    for (tmp_text = labels.first(); tmp_text != 0; tmp_text = labels.next()) {
      if (tmp_text->Start() == tmp_pt) {
	if (least_hindered_side < 0)
	  tmp_text->CheckAlignment(1); // left hindered
	else 
	  tmp_text->CheckAlignment(2); // right hindered
      }
    }
    // don't add if hydrogen already present
    if ( tmp_pt->element.contains("H") == 0) {
      h = MolData::Hydrogens(tmp_pt->element) - sumbonds;
      cout << h << endl;
      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);
      }
    }
    // look at some special cases
    if ( (tmp_pt->element == "MeO") || (tmp_pt->element == "OMe") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "MeO";
      else
	tmp_pt->element = "OMe";
    }
    if ( (tmp_pt->element == "EtO") || (tmp_pt->element == "OEt") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "EtO";
      else
	tmp_pt->element = "OEt";
    }
    if ( (tmp_pt->element == "AcO") || (tmp_pt->element == "OAc") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "AcO";
      else
	tmp_pt->element = "OAc";
    }
    if ( (tmp_pt->element == "TsO") || (tmp_pt->element == "OTs") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "TsO";
      else
	tmp_pt->element = "OTs";
    }
    if ( (tmp_pt->element == "COOH") || (tmp_pt->element == "HOOC") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "HOOC";
      else
	tmp_pt->element = "COOH";
    }
    if ( (tmp_pt->element == "OCH3") || (tmp_pt->element == "CH3O") ) {
      if (least_hindered_side < 0)
	tmp_pt->element = "CH3O";
      else
	tmp_pt->element = "OCH3";
    }
    // now find Text which reference this DPoint and copy back
    QString elbackup;
    if (orig_element != tmp_pt->element) {
      for (tmp_text = labels.first(); tmp_text != NULL; 
	   tmp_text = labels.next()) {
	if (tmp_text->Start() == tmp_pt) {
	  elbackup = tmp_pt->element;
	  cout << tmp_pt->element << endl;
	  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...)
  }
}

void Molecule::CalcIR() {
  QList<DPoint> up;
  int atom1, atom2, lorder, swp;

  // get list of unique points
  up = AllPoints();
  // find rings (esp. find aromaticity)
  MakeSSSR();

  GraphDialog *g = new GraphDialog(r, i18n("Predicted IR"));
  // iterate thru all Bonds in Molecule
  QListIterator<Bond> outer(bonds), inner(bonds);
  for ( ; outer.current(); ++outer) {
    // check for 'obvious' cases
    tmp_bond = outer.current();
    // rules for atoms and groups
    lorder = tmp_bond->Order();
    if (lorder > 3) lorder = 1;  // stereo bonds (5,7) are single bonds (1)
    atom1 = tmp_bond->Start()->getAtomicNumber();
    atom2 = tmp_bond->End()->getAtomicNumber();
    if (atom1 > atom2) { swp = atom1; atom1 = atom2; atom2 = swp; }
    if (lorder == 1) {
      if ( (atom1 == 6) && (atom2 == 7) )
	g->AddPeak(1300.0, i18n("C-N"), i18n("~1350-1000, C-N"));
      if ( (atom1 == 6) && (atom2 == 8) )
	g->AddPeak(1300.0, i18n("C-O"), i18n("~1300-1000, C-O"));
    }
    if (lorder == 2) {
      if ( (atom1 == 6) && (atom2 == 6) )
	g->AddPeak(1660.0, i18n("C=C"), i18n("~1660-1600, C=C (cis/vinyl strong; trans weak)"));
      if ( (atom1 == 6) && (atom2 == 7) )
	g->AddPeak(1670.0, i18n("C=N"), i18n("~1690-1640, C=N"));
      if ( (atom1 == 6) && (atom2 == 8) )
	g->AddPeak(1700.0, i18n("C=O"), i18n("~1700 (narrow), C=O"));
      if ( (atom1 == 8) && (atom2 == 16) )
	g->AddPeak(1350.0, i18n("S=O"), i18n("~1350-1300, S=O (~1050 if R-(S=O)-R')"));
    }
    if (lorder == 3) {
      if ( (atom1 == 6) && (atom2 == 7) )
	g->AddPeak(2250.0, i18n("CN"), i18n("~2250 (narrow), nitrile"));
    }

    /* ####################################################################
    if (tmp_bond->Order() == 1) {
      if ( (tmp_bond->Start()->element == "C") &&
	   (tmp_bond->End()->element.contains("O") > 0) ) {
	g->AddPeak(1300.0, QString("C-O"), QString("~1300-1000, C-O"));
      }
      if ( (tmp_bond->Start()->element.contains("O") > 0) &&
	   (tmp_bond->End()->element == "C") ) {
	g->AddPeak(1300.0, QString("C-O"), QString("~1300-1000, C-O"));
      }
      if ( (tmp_bond->Start()->element == "C") &&
	   (tmp_bond->End()->element == "N") ) {
	g->AddPeak(1300.0, QString("C-N"), QString("~1350-1000, C-N"));
      }
      if ( (tmp_bond->Start()->element == "N") &&
	   (tmp_bond->End()->element == "C") ) {
	g->AddPeak(1300.0, QString("C-N"), QString("~1350-1000, C-N"));
      }      
    }
    if (tmp_bond->Order() == 2) {
      if ( (tmp_bond->Start()->element == "C") &&
	   (tmp_bond->End()->element == "O") ) {  // ketone
	g->AddPeak(1700.0, QString("C=O"), QString("~1700 (narrow), ketone"));
      }
      if ( (tmp_bond->Start()->element == "O") &&
	   (tmp_bond->End()->element == "C") ) {  // ketone
	g->AddPeak(1700.0, QString("C=O"), QString("~1700 (narrow), ketone"));
      }
      if ( (tmp_bond->Start()->element == "C") &&
	   (tmp_bond->End()->element == "C") ) {  // carbon double bond
	g->AddPeak(1700.0, QString("C=C"), QString("~1660-1600, C=C (cis/vinyl strong; trans weak)"));
      }
    }
    if (tmp_bond->Order() == 3) {
      if ( (tmp_bond->Start()->element == "C") &&
	   (tmp_bond->End()->element == "N") ) {  // nitrile
	g->AddPeak(2250.0, QString("CN"), QString("~2250 (narrow), nitrile"));
      }
      if ( (tmp_bond->Start()->element == "N") &&
	   (tmp_bond->End()->element == "C") ) {  // nitrile
	g->AddPeak(2250.0, QString("CN"), QString("~2250 (narrow), nitrile"));
      }
    }
    #########################################################################
    */
  }
  // iterate thru unique atoms, look for functional groups
  for (tmp_pt = up.first(); tmp_pt != NULL; tmp_pt = up.next()) {
    cout << "|" << tmp_pt->element << "|" << endl;
    if ( (tmp_pt->element == "C") && (tmp_pt->substituents < 4) )
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "CH")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "HC")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "CH2")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "H2C")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "CH3")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if (tmp_pt->element == "H3C")
      g->AddPeak(3000.0, i18n("CH"), i18n("~3000 (broad), C-H"));
    if ( (tmp_pt->element == "N") && (tmp_pt->substituents == 1) )
      g->AddPeak(3350.0, i18n("NH"), i18n("two peaks: ~3400, ~3300, primary N-H"));
    if ( (tmp_pt->element == "N") && (tmp_pt->substituents == 2) )
      g->AddPeak(3300.0, i18n("NH"), i18n("~3300 (broad), secondary N-H"));
    if (tmp_pt->element == "NH")
      g->AddPeak(3300.0, i18n("NH"), i18n("~3300 (broad), secondary N-H"));
    if (tmp_pt->element == "HN")
      g->AddPeak(3300.0, i18n("NH"), i18n("~3300 (broad), secondary N-H"));
    if (tmp_pt->element == "NH2")
      g->AddPeak(3350.0, i18n("NH"), i18n("two peaks: ~3400, ~3300, primary N-H"));
    if (tmp_pt->element == "H2N")
      g->AddPeak(3350.0, i18n("NH"), i18n("two peaks: ~3400, ~3300, primary N-H"));
    if ( (tmp_pt->element == "S") && (tmp_pt->substituents < 2) )
      g->AddPeak(2550.0, i18n("SH"), i18n("~2550 (broad), S-H"));
    if ( (tmp_pt->element == "SH") && (tmp_pt->substituents < 2) )
      g->AddPeak(2550.0, i18n("SH"), i18n("~2550 (broad), S-H"));
    if ( (tmp_pt->element == "HS") && (tmp_pt->substituents < 2) )
      g->AddPeak(2550.0, i18n("SH"), i18n("~2550 (broad), S-H"));
    if ( (tmp_pt->element == "O") && (tmp_pt->substituents < 2) )
      g->AddPeak(2550.0, i18n("OH"), i18n("~3400 (broad), O-H"));
    if (tmp_pt->element == "CN")
      g->AddPeak(2250.0, i18n("CN"), i18n("~2250 (narrow), nitrile"));
    if (tmp_pt->element == "NC")
      g->AddPeak(2250.0, i18n("CN"), i18n("~2250 (narrow), nitrile"));
    if (tmp_pt->element == "NCO")
      g->AddPeak(2270.0, i18n("NCO"), i18n("~2270 (narrow), -N=C=O"));
    if (tmp_pt->element == "NCS")
      g->AddPeak(2125.0, i18n("NCS"), i18n("~2125 (narrow), -N=C=S"));
    if (tmp_pt->element == "OH")
      g->AddPeak(3400.0, i18n("OH"), i18n("~3400 (broad), O-H"));
    if (tmp_pt->element == "HO")
      g->AddPeak(3400.0, i18n("OH"), i18n("~3400 (broad), O-H"));
    if (tmp_pt->element == "NO2")
      g->AddPeak(1525.0, i18n("NO2"), i18n("~1525 (narrow), -NO2"));
    if (tmp_pt->element == "O2N")
      g->AddPeak(1525.0, i18n("NO2"), i18n("~1525 (narrow), -NO2"));
    if (tmp_pt->aromatic == true) {
      g->AddPeak(1600.0, i18n("aromatic"), i18n("~1600 (narrow), aromatic ring C=C"));
      g->AddPeak(1475.0, i18n("aromatic"), i18n("~1475 (narrow), aromatic ring C=C"));
    }
  }

  QPixmap mol = r->MakeFullPixmap();
  QRect rr1 = BoundingBoxAll();
  rr1.setLeft(rr1.left() - 4);
  rr1.setTop(rr1.top() - 4);
  rr1.setBottom(rr1.bottom() + 4);
  rr1.setRight(rr1.right() + 4);
  QPixmap mol1(rr1.size());
  bitBlt(&mol1, QPoint(0,0), &mol, rr1);

  g->AddPixmap(mol1);
  g->show();
}

