// moldata.h -- class MolData knows how to calculate molecular weights

#ifndef MOLDATA_H
#define MOLDATA_H

#include <qstring.h>
#include <qstringlist.h>
#include <iostream>
using std::cout;
using std::endl;

class MolData {
 public:
  static double NameToMW(QString e) {
    if (e.upper() == QString("H")) return 1.00794;
    if (e.upper() == QString("HE")) return 4.0026;
    if (e.upper() == QString("LI")) return 6.941;
    if (e.upper() == QString("B")) return 10.811;
    if (e.upper() == QString("C")) return 12.011;
    if (e.upper() == QString("N")) return 14.0067;
    if (e.upper() == QString("O")) return 15.9994;
    if (e.upper() == QString("F")) return 18.9984;
    if (e.upper() == QString("NA")) return 22.98977;
    if (e.upper() == QString("MG")) return 24.305;
    if (e.upper() == QString("SI")) return 28.0855;
    if (e.upper() == QString("P")) return 30.97376;
    if (e.upper() == QString("S")) return 32.066;
    if (e.upper() == QString("CL")) return 35.4527;
    if (e.upper() == QString("K")) return 39.0983;
    if (e.upper() == QString("SE")) return 78.96;
    if (e.upper() == QString("BR")) return 79.904;
    if (e.upper() == QString("I")) return 126.9045;
    return 6.0;
  }
  static double MW(QString x) { 
    cout << "Request for MW of " << x << endl;
    // parse this string
    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
    double this_mw; // MW of this element
    int repeat = 1; // number of this atom
    double mw_out = 0.0;  // MW returned
    QStringList tokens;
    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++;
      }
      if (isoflag)
	this_mw = iso.toDouble();
      else
	this_mw = NameToMW(thiselement);
      // add to molecular weight
      mw_out = mw_out + (repeat * this_mw);
      isoflag = false;
      repeat = 1;
    } while (ptr < x.length());
    cout << "MW = " << mw_out << endl;
    return mw_out;
  }
  // How many bonds (hydrogens) should this element or group have?
  static int Hydrogens(QString x) {
    //cout << "Request for bonds/hydrogens to |" << x << "|" << endl;
    // first check 'special' patterns
    if (x.upper().contains("TIPS") == 1) return 1;
    if (x.upper().contains("TMS") == 1) return 1;
    if (x.upper().contains("MS") == 1) return 1;
    if (x == "CO") return 2;
    if (x == "SO") return 2;
    if (x == "AcO") return 1;
    if (x == "MeO") return 1;
    if (x == "EtO") return 1;
    if (x == "TsO") return 1;
    if (x == "BnO") return 1;
    if (x == "BzO") return 1;
    if (x == "TBSO") return 1;
    if (x == "OAc") return 1;
    if (x == "OMe") return 1;
    if (x == "OEt") return 1;
    if (x == "OTs") return 1;
    if (x == "OBn") return 1;
    if (x == "OBz") return 1;
    if (x == "OTBS") return 1;
    if (x == "NO2") return 1;
    if (x == "O2N") return 1;
    if (x == "CH3") return 1;
    if (x == "OH") return 1;
    if (x == "HO") return 1;
    if (x == "CHO") return 1;
    if (x == "COOH") return 1;
    if (x == "HOOC") return 1;
    if (x == "CN") return 1;
    if (x == "NH2") return 1;
    if (x == "H2N") return 1;
    if (x == "NH") return 2;
    if (x == "HN") return 2;
    if (x == "O-") return 1;
    if (x == "-O") return 1;
    int flag = false;
    int ptr = 0;
    QString firstelement;  // element to consider
    // Find first letter
    do {
      if (x.at(ptr).isLetter() == false) 
	ptr++;
      else
	flag = true;
    } while (flag == false);
    firstelement.append(x.at(ptr));
    ptr++;
    if (x.at(ptr).category() == QChar::Letter_Lowercase) {
      firstelement.append(x.at(ptr));
      ptr++;
    }
    if (x.at(ptr).category() == QChar::Letter_Lowercase) {
      firstelement.append(x.at(ptr));
      ptr++;
    }
    if (firstelement.upper() == QString("H")) return 1;
    if (firstelement.upper() == QString("HE")) return 0;
    if (firstelement.upper() == QString("LI")) return 1;
    if (firstelement.upper() == QString("B")) return 3;
    if (firstelement.upper() == QString("C")) return 4;
    if (firstelement.upper() == QString("N")) return 3;
    if (firstelement.upper() == QString("O")) return 2;
    if (firstelement.upper() == QString("F")) return 1;
    if (firstelement.upper() == QString("NA")) return 1;
    if (firstelement.upper() == QString("MG")) return 2;
    if (firstelement.upper() == QString("SI")) return 4;
    if (firstelement.upper() == QString("CL")) return 1;
    if (firstelement.upper() == QString("K")) return 1;
    if (firstelement.upper() == QString("SE")) return 2;
    if (firstelement.upper() == QString("BR")) return 1;
    if (firstelement.upper() == QString("I")) return 1;
    // these could cause problems
    if (firstelement.upper() == QString("P")) return 3;
    if (firstelement.upper() == QString("S")) return 2;
    // if not known...
    return 1;
  }
};

#endif
