/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QtGui>
#include <QMessageBox>
#include <QFileDialog>


/////////////////////// Std includes
#include <math.h>
#include <algorithm>
#include <limits> // for std::numeric_limits

using namespace std;


/////////////////////// Local includes
#include <massxpert/gui/SpectrumCalculationDlg.hpp>
#include <libmass/IsotopicPatternCalculator.hpp>
#include <massxpert/gui/CalculatorWnd.hpp>


namespace msXpSmassXpert
{

enum
{
  TAB_WIDGET_INPUT_DATA,
  TAB_WIDGET_LOG,
  TAB_WIDGET_RESULTS,
};


SpectrumCalculationDlg::SpectrumCalculationDlg(
  QWidget *parent,
  const QString &configSettingsFilePath,
  const QList<msXpSlibmass::Atom *> &atomList,
  SpectrumCalculationMode mode)
  : QDialog{parent},
    m_mode{mode},
    m_atomList{atomList},
    m_configSettingsFilePath{configSettingsFilePath},
    m_polChemDef{static_cast<CalculatorWnd *>(parent)->polChemDef()}
{
  if(!parent)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  setWindowTitle("massXpert: Spectrum Calculator");

  m_aborted = false;

  // When the window is created the formula line edit is empty, so
  // at least that data has an error. The ionization data is most
  // probably not erroneous because it comes from the polymer
  // chemistry definition or from the caller.
  m_validationErrors = VALIDATION_FORMULA_ERRORS;

  m_filePath = "";

  m_ui.setupUi(this);

  // Set the oligomer list pointer to 0, so that we know if it was
  // set or not later.
  mp_oligomerList = 0;

  setupDialog();

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

  settings.beginGroup("isotopic_pattern_calculation_dlg");

  restoreGeometry(settings.value("geometry").toByteArray());

  m_ui.splitter->restoreState(settings.value("splitter").toByteArray());

  settings.endGroup();

  connect(m_ui.chargeSpinBox,
          SIGNAL(valueChanged(int)),
          this,
          SLOT(chargeChanged(int)));

  connect(m_ui.formulaLineEdit,
          SIGNAL(textEdited(const QString &)),
          this,
          SLOT(formulaEdited(const QString &)));

  connect(m_ui.pointNumberSpinBox,
          SIGNAL(valueChanged(int)),
          this,
          SLOT(pointsChanged(int)));

  connect(m_ui.resolutionSpinBox,
          SIGNAL(valueChanged(int)),
          this,
          SLOT(resolutionChanged(int)));

  connect(m_ui.fwhmLineEdit,
          SIGNAL(textEdited(const QString &)),
          this,
          SLOT(fwhmEdited(const QString &)));

  connect(m_ui.monoRadioButton,
          SIGNAL(toggled(bool)),
          this,
          SLOT(massTypeRadioButtonToggled(bool)));

  connect(m_ui.avgRadioButton,
          SIGNAL(toggled(bool)),
          this,
          SLOT(massTypeRadioButtonToggled(bool)));

  connect(m_ui.avgRadioButton,
          SIGNAL(toggled(bool)),
          this,
          SLOT(massTypeRadioButtonToggled(bool)));

  connect(m_ui.isotopicClusterCheckBox,
          SIGNAL(toggled(bool)),
          this,
          SLOT(isotopicClusterCheckBoxToggled(bool)));

  connect(m_ui.executePushButton, SIGNAL(clicked()), this, SLOT(execute()));

  connect(m_ui.abortPushButton, SIGNAL(clicked()), this, SLOT(abort()));

  connect(
    m_ui.outputFilePushButton, SIGNAL(clicked()), this, SLOT(outputFile()));
}


void
SpectrumCalculationDlg::closeEvent(QCloseEvent *event)
{
  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

  settings.beginGroup("isotopic_pattern_calculation_dlg");

  settings.setValue("geometry", saveGeometry());

  settings.setValue("splitter", m_ui.splitter->saveState());

  settings.endGroup();
}


SpectrumCalculationDlg::~SpectrumCalculationDlg()
{
  while(m_patternPointList.size())
    delete m_patternPointList.takeFirst();
}


void
SpectrumCalculationDlg::setOligomerList(OligomerList *list)
{
  if(!list)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_oligomerList = list;
}


void
SpectrumCalculationDlg::setupDialog()
{
  // Always start the dialog with the first page of the tab widget,
  // the input data page.
  m_ui.tabWidget->setCurrentIndex(TAB_WIDGET_INPUT_DATA);

  // By default we want a gaussian-type shape.
  m_config.setPeakShapeType(
    msXpSlibmass::PeakShapeType::PEAK_SHAPE_TYPE_GAUSSIAN);

  // Throughout of *this* dialog window, the normalization factor
  // for the peak shapes (Gaussian, specifically) is going to be 1.

  // Charge stuff.
  m_ui.chargeSpinBox->setRange(1, 10000000);
  m_ui.chargeSpinBox->setValue(1);
  m_charge = m_ui.chargeSpinBox->value();

  // Isotopic peak probability stuff.
  m_ui.minimumProbabilityLineEdit->setText("0.0000001");
  m_minimumProbability = 0.0000001;

  // Max number of peaks in the isotopic cluster.
  m_ui.maximumPeaksSpinBox->setRange(0, 1000);
  m_ui.maximumPeaksSpinBox->setValue(100);
  m_maximumPeaks = 100;

  // Resolution and FWHM stuff
  m_ui.resolutionSpinBox->setRange(0, 1000000);
  m_ui.resolutionSpinBox->setValue(0);
  m_resolution = 0;

  // Get the number of points used to craft the curve.
  m_config.setPointNumber(m_ui.pointNumberSpinBox->value());

  // Since both values above are not correct, set the corresponding
  // bits to the validation error int.
  m_validationErrors |= VALIDATION_RESOLUTION_ERRORS;
  m_validationErrors |= VALIDATION_FWHM_ERRORS;

  // Now, depending on the kind of operation required for the
  // dialog, adapt some behaviour.


  if(m_mode == SPECTRUM_CALCULATION_MODE_CLUSTER)
    {
      // In isotopic cluster calculation mode, the m/z calculated
      // starting from the formula and the charge is of course mono!
      // Thus, we set it checked and disable the enclosing group box.

      m_ui.monoRadioButton->setChecked(true);
      m_ui.massTypeGroupBox->setDisabled(true);

      // Of course, the computeIsotopicClusterCheckBox should be
      // checked and disabled.
      m_ui.isotopicClusterCheckBox->setChecked(true);
      m_ui.isotopicClusterCheckBox->setDisabled(true);

      // And all the related widgets should be active;

      m_ui.pointNumberSpinBox->setDisabled(false);
      m_ui.minimumProbabilityLineEdit->setDisabled(false);
      m_ui.maximumPeaksSpinBox->setDisabled(false);
    }
  else if(m_mode == SPECTRUM_CALCULATION_MODE_SPECTRUM)
    {
      // In spectrum mode, the formula, z and m/z are not active.
      m_ui.mzGroupBox->setDisabled(true);

      // By default, we work on mono masses.
      m_withMonoMass = true;
      m_ui.monoRadioButton->setChecked(m_withMonoMass);

      // By default, we do not want isotopic cluster calculations.
      m_withCluster = false;
      m_ui.isotopicClusterCheckBox->setChecked(m_withCluster);

      // Not willing to compute isotopic clusters, thus all the
      // related widgets should be inactive.
      m_ui.pointNumberSpinBox->setDisabled(true);
      m_ui.minimumProbabilityLineEdit->setDisabled(true);
      m_ui.maximumPeaksSpinBox->setDisabled(true);
    }
}


bool
SpectrumCalculationDlg::fetchValidateInputData()
{
  if(m_mode == SPECTRUM_CALCULATION_MODE_CLUSTER)
    return fetchValidateInputDataIsotopicClusterMode();
  else if(m_mode == SPECTRUM_CALCULATION_MODE_SPECTRUM)
    return fetchValidateInputDataSpectrumMode();
  else
    return false;
}


bool
SpectrumCalculationDlg::fetchValidateInputDataIsotopicClusterMode()
{
  // The ionization stuff, the formula for which the isotopic
  // pattern is to be calculated, the mz ratio (in m_mono) all have
  // been checked previously by automated procedures (see the
  // "changed" slots). We have to make sure that either the
  // resolution or the FWHM values are OK. All the other bits must
  // be clear. Only either the RESOLUTION or the FWHM bit might be
  // set.

  int testBitset = 0;
  testBitset |= VALIDATION_RESOLUTION_ERRORS;
  testBitset |= VALIDATION_FWHM_ERRORS;

  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      testBitset, "testBitset");

  if(m_validationErrors == testBitset)
    return false;

  if((m_validationErrors & VALIDATION_FORMULA_ERRORS) ==
     VALIDATION_FORMULA_ERRORS)
    return false;

  // Now that we know that the formula was validated, we can ask
  // that all the isotopes be deep-copied into the formula.

  // The list of atomCount objects has to be made with fully detailed
  // isotopes...
  if(!m_formula.deepAtomCopy(m_atomList))
    {
      QMessageBox::warning(0,
                           tr("massXpert: Spectrum Calculator"),
                           tr("Failed to deep-copy atom list."),
                           QMessageBox::Ok);

      return false;
    }

  // We still have to fetch a number of parameters.

  // Get to know if we want gaussian or lorentzian shapes.
  if(m_ui.gaussianRadioButton->isChecked())
    {
      m_config.setPeakShapeType(
        msXpSlibmass::PeakShapeType::PEAK_SHAPE_TYPE_GAUSSIAN);
    }
  else
    {
      m_config.setPeakShapeType(
        msXpSlibmass::PeakShapeType::PEAK_SHAPE_TYPE_LORENTZIAN);
    }

  int totAtoms = m_formula.totalAtoms();
  m_ui.progressBar->setRange(0, totAtoms);
  m_ui.progressBar->setValue(0);

  int totIsotopes = m_formula.totalIsotopes(m_atomList);

  QString textEditText;

  textEditText +=
    tr(
      "INPUT\n=====\n\n"
      "Formula: %1\n"
      "Charge: %2\n"
      "Mono Mass: %3 \t Avg mass: %4\n"
      "Total number of atoms: %5\t"
      "Total number of isotopes: %6\n\n")
      .arg(m_formula.text())
      .arg(m_charge)
      .arg(QString().setNum(m_mono, 'f', msXpS::OLIGOMER_DEC_PLACES))
      .arg(QString().setNum(m_avg, 'f', msXpS::OLIGOMER_DEC_PLACES))
      .arg(totAtoms)
      .arg(totIsotopes);

  // Put that description in the text edit widget.
  m_ui.resultPlainTextEdit->appendPlainText(textEditText);

  // qDebug() << __FILE__ << __LINE__
  //          << textEditText;

  // At this point we have to sort out wether the user wants to use
  // the resolution or the FWHM for the calculations. This is
  // automatically known by looking at which value is 0 (see the
  // corresponding changed() slots).

  // Get the points value.
  m_config.setPointNumber(m_ui.pointNumberSpinBox->value());

  // How about setting the increment, that is the size of the gap
  // between two points ? If it is not set (its value is 0), then
  // increment is fwhm() / m_pointNumber;

  // First however, see if the user wants to use the FWHM value or
  // the resolution.

  if(m_config.fwhm())
    {
      // FWHM is not zero, which means the user has set a value for
      // it. Keep it.
    }
  else
    {
      // We have to compute the FWHM starting from the mz ratio and
      // the resolution.

      m_config.setFwhm(m_mono / m_resolution);
    }

  // Set the maximum number of peaks in the isotopic curve.
  m_maximumPeaks = m_ui.maximumPeaksSpinBox->value();

  // Set the minimum probability each isotopic peak must have to be
  // retained in the final curve.

  QString minProbString = m_ui.minimumProbabilityLineEdit->text();
  bool ok               = false;

  double minProb = minProbString.toDouble(&ok);

  if(!minProb && !ok)
    {
      QMessageBox::warning(0,
                           tr("massXpert: Spectrum Calculator"),
                           tr("Please, fix the minimum probability."),
                           QMessageBox::Ok);

      return false;
    }

  m_minimumProbability = minProb;

  return true;
}


bool
SpectrumCalculationDlg::fetchValidateInputDataSpectrumMode()
{
  // The ionization stuff, the formula for which the isotopic
  // pattern is to be calculated, the mz ratio (in m_mono) all have
  // been checked previously by automated procedures (see the
  // "changed" slots). We have to make sure that either the
  // resolution or the FWHM values are OK. All the other bits must
  // be clear. Only either the RESOLUTION or the FWHM bit might be
  // set.

  int testBitset = 0;
  testBitset |= VALIDATION_RESOLUTION_ERRORS;
  testBitset |= VALIDATION_FWHM_ERRORS;

  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      testBitset, "testBitset");

  if(m_validationErrors == testBitset)
    return false;

  // We do not care of the formula in our spectrum calculation
  // situation. Each oligomer from a cleavage contains a chemical
  // composition formula that we'll use each time.

  // We still have to fetch a number of parameters.

  // Are we working on the mono or the avg masses of the oligomers?

  m_withMonoMass = m_ui.monoRadioButton->isChecked();

  // Get to know if we want gaussian or lorentzian shapes.
  if(m_ui.gaussianRadioButton->isChecked())
    {
      m_config.setPeakShapeType(
        msXpSlibmass::PeakShapeType::PEAK_SHAPE_TYPE_GAUSSIAN);
    }
  else
    {
      m_config.setPeakShapeType(
        msXpSlibmass::PeakShapeType::PEAK_SHAPE_TYPE_LORENTZIAN);
    }

  // At this point we have to sort out wether the user wants to use
  // the resolution or the FWHM for the calculations. This is
  // automatically known by looking at which value is 0 (see the
  // corresponding changed() slots).

  // Get the points value.
  m_config.setPointNumber(m_ui.pointNumberSpinBox->value());

  // How about setting the increment, that is the size of the gap
  // between two points ? If it is not set (its value is 0), then
  // increment is fwhm() / m_pointNumber;

  // First however, see if the user wants to use the FWHM value or
  // the resolution.

  if(m_config.fwhm())
    {
      // FWHM is not zero, which means the user has set a value for
      // it. Keep it.
    }
  else
    {
      // We cannot compute fwhm starting from the resolution because
      // we cannot know what is the m/z of the oligomers beforehand.

      // All we can check is that resolution is non-zero.

      if(m_resolution <= 0)
        {
          QMessageBox::warning(0,
                               tr("massXpert: Spectrum Calculator"),
                               tr("Please, fix the resolution or the FWHM."),
                               QMessageBox::Ok);

          return false;
        }
    }

  // Set the maximum number of peaks in the isotopic curve.
  m_maximumPeaks = m_ui.maximumPeaksSpinBox->value();

  // Set the minimum probability each isotopic peak must have to be
  // retained in the final curve.

  QString minProbString = m_ui.minimumProbabilityLineEdit->text();
  bool ok               = false;

  double minProb = minProbString.toDouble(&ok);

  if(!minProb && !ok)
    {
      QMessageBox::warning(0,
                           tr("massXpert: Spectrum Calculator"),
                           tr("Please, fix the minimum probability."),
                           QMessageBox::Ok);

      return false;
    }

  m_minimumProbability = minProb;

  return true;
}


// Returns false if formula is bad or aborts if calculation fails..
bool
SpectrumCalculationDlg::fetchFormulaMass(double *mono, double *avg)
{
  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ fetchFormulaMass");

  // Check if there are currently errors set.

  if((m_validationErrors & VALIDATION_FORMULA_ERRORS) ==
     VALIDATION_FORMULA_ERRORS)
    return false;

  double monoMass = 0;
  double avgMass  = 0;

  // It is impossible that we have an error here, since the formula
  // was previously validated (VALIDATION_FORMULA_ERRORS above).
  if(!m_formula.accountMasses(m_atomList, &monoMass, &avgMass, 1))
    qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);

  if(mono)
    *mono = monoMass;

  if(avg)
    *avg = avgMass;

  // qDebug() << __FILE__ << __LINE__
  //          << "Formula mono/avg masses:"
  //          << monoMass << "/" << avgMass;

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ fetchFormulaMass");

  return true;
}

// Returns false if the formula is not validated and aborts if an
// error occurs while the formula was validated.
bool
SpectrumCalculationDlg::fetchMzRatio(double *mono, double *avg)
{
  // Check if there are currently errors set either in the formula
  // or in the ionization rule.

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ ENTER fetchMzRatio");

  if((m_validationErrors & VALIDATION_FORMULA_ERRORS) ==
     VALIDATION_FORMULA_ERRORS)
    {
      return false;
    }

  // The formula data have already been automatically fetched and
  // set to m_formula when the data in the widgets changed using the
  // correponding slot.

  // We can compute the m/z ratio.

  double monoMass = 0;
  double avgMass  = 0;

  fetchFormulaMass(&monoMass, &avgMass);

  // qDebug() << __FILE__ << __LINE__
  //          << "monoMass:" << monoMass
  //          << "avgMass:" << avgMass;

  // Note that the only acceptable value returned here is mono > 0,
  // because the formula was validated and thus mass calculation
  // should not fail. Indeed, if there is a failure in the called
  // function, qFatal is triggered. No need, thus, to check the
  // status of the call. It cannot fail.

  // Compute the m/z ratio.
  monoMass = monoMass / m_charge;
  avgMass  = avgMass / m_charge;

  // At this point the ionizable has the right mzRatio.

  if(mono)
    *mono = monoMass;

  if(avg)
    *avg = avgMass;

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ EXIT fetchMzRatio");

  return true;
}

void
SpectrumCalculationDlg::massTypeRadioButtonToggled(bool checked)
{
  Q_UNUSED(checked);

  m_withMonoMass = m_ui.monoRadioButton->isChecked();

  // If the mass type is average, then, of course, no isotopic
  // cluster calculation can be required.

  if(!m_withMonoMass)
    m_ui.isotopicClusterCheckBox->setChecked(false);
}

void
SpectrumCalculationDlg::isotopicClusterCheckBoxToggled(bool checked)
{
  m_withCluster = checked;

  if(checked)
    {
      // If the isotopic cluster is to be computed for each oligomer,
      // then that means necessarily that the mass is mono!

      m_withMonoMass = true;
      m_ui.monoRadioButton->setChecked(true);

      m_ui.pointNumberSpinBox->setDisabled(false);
      m_ui.minimumProbabilityLineEdit->setDisabled(false);
      m_ui.maximumPeaksSpinBox->setDisabled(false);
    }
  else
    {
      // Not willing to compute isotopic clusters, thus all the
      // related widgets should be inactive.

      m_ui.pointNumberSpinBox->setDisabled(true);
      m_ui.minimumProbabilityLineEdit->setDisabled(true);
      m_ui.maximumPeaksSpinBox->setDisabled(true);
    }
}


void
SpectrumCalculationDlg::formulaEdited(const QString &text)
{
  // We only handle the automatic recalculations on the basis of
  // this change if we are in a isotopic cluster calculation task.
  if(m_mode != SPECTRUM_CALCULATION_MODE_CLUSTER)
    return;

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ ENTER formulaChanged");

  // qDebug() << __FILE__ << __LINE__
  //          << "formulaChanged has text:" << text;

  // New text contains a new formula. Check it.

  if(text.isEmpty())
    {
      // Set the error bit.
      m_validationErrors |= VALIDATION_FORMULA_ERRORS;

      m_ui.monoMzRatioLineEdit->setText("0.000");
      m_ui.inputDataFeedbackLineEdit->setText(tr("Formula error"));

      return;
    }

  msXpSlibmass::Formula formula(text);

  // Do not bother storing all the atoms in the formula, this is
  // only a check (false param below).  m_atomList is a reference to
  // the atom list of the polymer chemistry definition currently
  // used in the caller calculator window.

  if(!formula.validate(m_atomList, false, true))
    {
      // Set the error bit.
      m_validationErrors |= VALIDATION_FORMULA_ERRORS;

      m_ui.monoMzRatioLineEdit->setText("0.000");
      m_ui.inputDataFeedbackLineEdit->setText(tr("Formula error"));

      return;
    }

  // qDebug() << __FILE__ << __LINE__
  //          << "Formula:" << formula.formula() << "validated." ;

  // At this point we know we could actually validate the formula,
  // so do that work with the member formula:

  // We want to validate the formula and in the mean time construct
  // the list of all the AtomCount objects(first true), and since
  // the formula is reused we also ensure that that list is reset
  // (second true). m_atomList is a reference to the atom list of
  // the polymer chemistry definition currently used in the caller
  // calculator window.

  m_formula.setFormula(text);
  m_formula.validate(m_atomList, true, true);

  // Clear the bit as there is no error here.
  m_validationErrors &= ~VALIDATION_FORMULA_ERRORS;

  m_ui.inputDataFeedbackLineEdit->setText(tr("Formula fine"));

  // qDebug() << __FILE__ << __LINE__
  //          << "Going to call updateMzRatio.";

  updateMzRatio();
  updateIncrement();

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ EXIT formulaChanged");

  return;
}


void
SpectrumCalculationDlg::chargeChanged(int value)
{
  // We only handle the automatic recalculations on the basis of
  // this change if we are in a isotopic cluster calculation task.
  if(m_mode != SPECTRUM_CALCULATION_MODE_CLUSTER)
    return;

  // The charge has changed, we should compute the mzRatio.
  m_charge = m_ui.chargeSpinBox->value();

  updateMzRatio();
  updateIncrement();
}


void
SpectrumCalculationDlg::pointsChanged(int value)
{
  // We only handle the automatic recalculations on the basis of
  // this change if we are in a isotopic cluster calculation task.
  if(m_mode != SPECTRUM_CALCULATION_MODE_CLUSTER)
    return;

  // The points have changed, we should compute the increment.
  m_config.setPointNumber(m_ui.pointNumberSpinBox->value());

  updateIncrement();
}


void
SpectrumCalculationDlg::resolutionChanged(int value)
{
  // If the value changed, that means that the user wants the
  // resolution to be taken into account for calculation of the peak
  // width, and not the FWHM. Only if resolution is set to 0, FWHM
  // will be the value taken into account.

  // Show what's the value of the errors.

  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ ENTER resolutionChanged");

  if(value <= 0)
    {
      // Tell the user to set a valid FWHM value, then.
      m_ui.inputDataFeedbackLineEdit->setText(tr("Set a valid FWHM value"));

      m_ui.resolutionSpinBox->setValue(0);

      // Set the error bit so that we can let the increment
      // calculation know that we can not perform that computation
      // using FWHM.

      m_validationErrors |= VALIDATION_RESOLUTION_ERRORS;

      return;
    }

  m_resolution = value;

  m_ui.inputDataFeedbackLineEdit->setText(tr("Will use the resolution"));

  // Clear the bit as there is no error here.
  m_validationErrors &= ~VALIDATION_RESOLUTION_ERRORS;

  // We only handle the automatic recalculations on the basis of
  // this change if we are in a isotopic cluster calculation task
  // because only in that case do we know before starting the
  // calculation what will be the mz.
  if(m_mode != SPECTRUM_CALCULATION_MODE_CLUSTER)
    return;

  // We can try to compute the fwhm value corresponding to the
  // current mono m/z value and the resolution.

  double mono;
  double avg;

  if(fetchFormulaMass(&mono, &avg))
    {
      QString value;
      value.setNum(mono / m_resolution, 'f', msXpS::OLIGOMER_DEC_PLACES);

      // Note that the setText call below will not trigger automatic
      // fwhm setting because the lineEdit widget is connected to a
      // textEdited signal, not a textChanged signal.

      m_ui.fwhmLineEdit->setText(value);
    }

  // Now, because we want the resolution to be the governing
  // parameter when validation will occur, we do this:

  m_config.setFwhm(0);
  m_validationErrors |= VALIDATION_FWHM_ERRORS;

  // Show what's the value of the errors.

  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ EXIT resolutionChanged");

  updateIncrement();

  return;
}


void
SpectrumCalculationDlg::fwhmEdited(const QString &text)
{
  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ ENTER fwhmEdited");

  // If the text was edited in the line edit, that means that the
  // user wants the FWHM to be taken into account for calculation of
  // the peak width, and not the resolution. Only if FWHM is set to
  // 0, resolution will be the value taken into account.

  QString txt = m_ui.fwhmLineEdit->text();

  bool ok = false;

  double fwhmValue = txt.toDouble(&ok);

  if(!fwhmValue && !ok)
    {
      // Set the error bit.
      m_validationErrors |= VALIDATION_FWHM_ERRORS;

      m_ui.inputDataFeedbackLineEdit->setText(tr("Fix the FWHM value, please"));

      return;
    }

  // But the value might be faithfully 0, if the user is telling us
  // that she wants to take the resolution into account and not the
  // FWHM.

  if(!fwhmValue)
    {
      m_ui.inputDataFeedbackLineEdit->setText(
        tr("Set a valid resolution value"));

      // Set the error bit to let the increment calculation know
      // that it cannot base the calculation on the FWHM value.

      m_validationErrors |= VALIDATION_FWHM_ERRORS;

      return;
    }

  // At this point we know that fwhmValue contains a proper value.

  m_ui.inputDataFeedbackLineEdit->setText(tr("Will use the FWHM"));

  m_validationErrors &= ~VALIDATION_FWHM_ERRORS;

  m_config.setFwhm(fwhmValue);

  // Set the resolution spinbox to 0 and set the error bit
  // associated with it to let the increment calculation know that
  // it cannot be based on that value.

  m_resolution = 0;
  m_ui.resolutionSpinBox->setValue(0);
  m_validationErrors |= VALIDATION_RESOLUTION_ERRORS;

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ EXIT fwhmEdited");

  // We only handle the automatic recalculation of the increment on
  // the basis of this change if we are in a isotopic cluster
  // calculation task because only in this case do we know the mono
  // mz value.
  if(m_mode != SPECTRUM_CALCULATION_MODE_CLUSTER)
    return;

  updateIncrement();

  return;
}


bool
SpectrumCalculationDlg::updateMzRatio()
{
  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ ENTER updateMzRatio");

  // Set the mz ratio to our member data variable.
  bool res = fetchMzRatio(&m_mono, &m_avg);

  // If res is falsed, that means that the formula is not validated
  // at present. Silently return.
  if(!res)
    {
      QMessageBox::warning(0,
                           tr("massXpert - Spectrum Calculator"),
                           tr("Failed to fetch the mz ratio."),
                           QMessageBox::Ok);

      return false;
    }

  QString mzRatio;
  mzRatio.setNum(m_mono, 'f', msXpS::OLIGOMER_DEC_PLACES);

  m_ui.monoMzRatioLineEdit->setText(mzRatio);

  // Show what's the value of the errors.
  // debugPutStdErrBitset(__FILE__, __LINE__,
  //                      m_validationErrors,
  //                      "m_validationErrors @ EXIT updateMzRatio");

  return true;
}


bool
SpectrumCalculationDlg::updateIncrement()
{
  // We can only calculate the increment if we have the number of
  // points to construct the curve and either the resolution and the
  // mzRatio or the FWHM.

  // If we know the FWHM, then, fine, we can compute the increment
  // right away.

  double increment = 0;

  if((m_validationErrors & VALIDATION_FWHM_ERRORS) != VALIDATION_FWHM_ERRORS)
    {
      increment = (msXpS::FWHM_PEAK_SPAN_FACTOR * m_config.fwhm()) /
                  m_config.pointNumber();
    }
  else if((m_validationErrors & VALIDATION_RESOLUTION_ERRORS) !=
          VALIDATION_RESOLUTION_ERRORS)
    {
      // If we know the resolution and we can get the mzRatio, we'll
      // be able to first compute the FWHM as (mzRatio / FWHM). From
      // there, we'll be able to compute the increment.

      double mono = 0;
      double avg  = 0;

      // We compute the increment by using the mass of the formula,
      // not its mzRatio.
      if(fetchFormulaMass(&mono, &avg))
        {
          double fwhm = mono / m_resolution;

          increment =
            (msXpS::FWHM_PEAK_SPAN_FACTOR * fwhm) / m_config.pointNumber();

          // Because we'll need more points when the charge
          // increases... Formally, we have to check that charge is
          // not 0, but we should not be required to do it because
          // the charge can never be 0 (gui-limited values for the
          // charge spin box).
          if(m_charge)
            increment = increment / m_charge;
        }
    }

  if(increment)
    {
      m_config.setIncrement(increment);

      QString text;
      text.setNum(m_config.increment());

      m_ui.incrementLabel->setText(text);

      return true;
    }
  else
    m_ui.incrementLabel->setText("0");

  return false;
}


void
SpectrumCalculationDlg::execute()
{
  // We might be called to perform two different things:
  //
  // 1. Calculate a single istotopic cluster pattern.
  //
  // 2. Calculate a spectrum for a whole set of oligomers.

  if(m_mode == SPECTRUM_CALCULATION_MODE_CLUSTER)
    return executeCluster();
  else if(m_mode == SPECTRUM_CALCULATION_MODE_SPECTRUM)
    return executeSpectrum();
}


void
SpectrumCalculationDlg::executeCluster()
{
  // Clear the textEdit widget so that we can start out-putting text
  // into it, note that fetchValidateInputData() puts text in it.
  m_ui.resultPlainTextEdit->clear();

  // Clear the results string for the creation of the calculator
  // below to receive an empty string.
  m_resultsString.clear();

  // We are asked to launch the calculation. But we ought to make
  // sure that all the required data are set fine. The call below
  // will fill-in the m_config data.

  if(!fetchValidateInputData())
    {
      QMessageBox::warning(0,
                           tr("massXpert - Spectrum Calculator"),
                           tr("Please fix the input data first."),
                           QMessageBox::Ok);
      return;
    }

  // Before starting the calculations, swith to the log tab so that
  // the user can monitor what's going on.

  m_ui.tabWidget->setCurrentIndex(TAB_WIDGET_LOG);

  // Send to both the log and result text edit widget the data we
  // are going to use for the calculation.

  QString *text = new QString();

  text->append(tr("FWHM: %1 \t Max. peaks %2 "
                  "\t Min. probability: %3\n\n")
                 .arg(QString().setNum(m_config.fwhm(), 'f', 4))
                 .arg(QString().setNum(m_maximumPeaks))
                 .arg(QString().setNum(m_minimumProbability, 'f', 15)));

  m_ui.logPlainTextEdit->appendPlainText(*text);
  m_ui.resultPlainTextEdit->appendPlainText(*text);

  // Free the string, we will reuse it.
  delete text;

  // We will allocate a IsotopicPatternCalculator instance and let
  // it do the work.

  msXpSlibmass::IsotopicPatternCalculator *calculator =
    new msXpSlibmass::IsotopicPatternCalculator(m_formula,
                                                m_charge,
                                                m_maximumPeaks,
                                                m_minimumProbability,
                                                m_atomList,
                                                m_config);

  connect(calculator,
          SIGNAL(isotopicCalculationProgressValueChanged(int)),
          this,
          SLOT(spectrumCalculationProgressValueChanged(int)));

  connect(calculator,
          SIGNAL(isotopicCalculationMessageChanged(QString)),
          this,
          SLOT(spectrumCalculationMessageChanged(QString)));

  connect(this,
          SIGNAL(spectrumCalculationAborted()),
          calculator,
          SLOT(spectrumCalculationAborted()));


  // Perform the calculation...
  calculator->sumPeakShapes();

  // Get the list of peaks as "1251.14 0.025" pairs.
  text = calculator->peakCentroidListAsString();

  // Now prepend a header:

  text->prepend("\nList of peak centroids:\n");

  // Display the data to the user.
  m_ui.resultPlainTextEdit->appendPlainText(*text);

  // We want to get the calculation configuration.

  // Let's get the detailed calculation configuration
  // string, so that we can append it to the textEdit
  // widget.
  text->clear();
  text->append(m_config.config());
  m_ui.resultPlainTextEdit->appendPlainText(*text);

  // Finally delete the text.
  delete text;

  // Finally, if the user had asked for the data to be sent to a
  // file... or to a string... Get a reference to the list of
  // QPointF instances in the calculator and send them either to the
  // file or to the text edit widget.

  const QList<QPointF *> &spectrumPoints = calculator->pointList();
  QString dataString;

  if(!m_filePath.isEmpty())
    {
      // Send all the data (that is, the points located in
      // QList<QPointF *>m_patternPointList) to the output file.

      QFile file(m_filePath);

      if(!file.open(QFile::WriteOnly | QFile::Truncate))
        {
          QMessageBox::warning(this,
                               tr("massXpert - Spectrum Calculator"),
                               tr("Failed to export the isotopic pattern "
                                  "data to file."),
                               QMessageBox::Ok);
          return;
        }

      QTextStream stream(&file);

      for(int iter = 0, size = spectrumPoints.size(); iter < size; ++iter)
        {
          QPointF *point = spectrumPoints.at(iter);

          dataString = QString().setNum(point->x(), 'f', 10);
          dataString += " ";
          dataString += QString().setNum(point->y(), 'f', 10);
          dataString += "\n";
          stream << dataString;
          // qDebug() << __FILE__ << __LINE__ << dataString;
        }

      stream.flush();
      file.close();

      // At this moment, tell the user in the log that the data have
      // been written to the given file.

      QString message;

      message +=
        tr("\n\nData have been written to file %1\n\n").arg(m_filePath);

      message += tr("\n\nAlso check the results tab\n\n");

      m_ui.logPlainTextEdit->appendPlainText(message);

      return;
    }
  else
    {
      for(int iter = 0, size = spectrumPoints.size(); iter < size; ++iter)
        {
          QPointF *point = spectrumPoints.at(iter);

          dataString += QString().setNum(point->x(), 'f', 10);
          dataString += " ";
          dataString += QString().setNum(point->y(), 'f', 10);
          dataString += "\n";
        }

      m_ui.resultPlainTextEdit->appendPlainText(dataString);

      QClipboard *clipboard = QGuiApplication::clipboard();

      clipboard->setText(dataString);

      // At this moment, tell the user in the log that the data have
      // been written to result tab.

      QString message;

      message += tr(
        "\n\nData have been written to the results tab and copied to the "
        "clipboard for you to plot them with mineXpert.\n\n");

      m_ui.logPlainTextEdit->appendPlainText(message);
    }

  delete calculator;
}


void
SpectrumCalculationDlg::executeSpectrum()
{
  // Clear the textEdit widget so that we can start out-putting text
  // into it, note that fetchValidateInputData() puts text in it.
  m_ui.resultPlainTextEdit->clear();
  m_ui.logPlainTextEdit->clear();

  // Verify that there are oligomers in the list.

  if(!mp_oligomerList)
    return;

  if(mp_oligomerList->isEmpty())
    return;

  if(!fetchValidateInputData())
    {
      QMessageBox::warning(0,
                           tr("massXpert - Spectrum Calculator"),
                           tr("Please fix the input data first."),
                           QMessageBox::Ok);
      return;
    }

  // List of lists of QPointF, that is list of spectra.
  QList<QList<QPointF *> *> spectrumList;

  // Each time a new spectrum is calculated, store the corresponding
  // increment we'll need later to computed the summed final
  // spectrum.
  QHash<QList<QPointF *> *, double> incrementHash;

  // Before starting the calculations, swith to the log tab so that
  // the user can monitor what's going on.

  m_ui.tabWidget->setCurrentIndex(TAB_WIDGET_LOG);

  if(m_withCluster)
    executeSpectrumWithCluster(&spectrumList, &incrementHash);
  else
    executeSpectrumWithoutCluster(&spectrumList, &incrementHash);


  // At this point, with one method or the other, we should have as
  // many allocated QList<QPointF *> * instances (that is, spectrum
  // instances) as there are oligomers. Make that sanity check.

  if(spectrumList.isEmpty())
    return;

  if(spectrumList.size() < mp_oligomerList->size())
    qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);
  if(spectrumList.size() < incrementHash.size())
    qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);

  // qDebug() << __FILE__ << __LINE__
  //          << "Done generating the unitary spectra for each oligomer.";

  // Before computing the sum of all spectra to yield the last
  // spectrum, we have to perform the ordering of the spectra so
  // that the m/z regions covered by each are in an ascending order.

  // We want to sort the spectra in ascending order. There are two
  // concepts:

  // 1. one spectrum is greater than the other if its first point is
  //  greater than the other's.

  // 2. one spectrum is greater than the other, even if their first
  // points are equal, if its last point is greater then the
  // other's. That means two spectra starting at the same point are
  // different if they end at different points and the longest
  // spectrum sorts after the shortest.

  // Only sort if there are at least two spectra !

  m_ui.logPlainTextEdit->appendPlainText(
    tr("Sorting all oligomer-related sub-spectra..."));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  QApplication::processEvents();

  if(spectrumList.size() >= 2)
    {
      bool sorted = false;

      while(!sorted)
        {
          bool changed = false;

          for(int iter = 0; iter < spectrumList.size() - 1; ++iter)
            {
              QList<QPointF *> *thisSpectrum = spectrumList.at(iter);
              QList<QPointF *> *nextSpectrum = spectrumList.at(iter + 1);

              QPointF *thisFirstPoint = thisSpectrum->first();
              QPointF *thisLastPoint  = thisSpectrum->last();

              QPointF *nextFirstPoint = nextSpectrum->first();
              QPointF *nextLastPoint  = nextSpectrum->last();

              if(thisFirstPoint->x() > nextFirstPoint->x())
                {
                  // Reverse the spectra in the list, as this spectrum
                  // is farther away in the mz axis than is the next
                  // spectrum.

                  int thisIdx = iter;
                  int nextIdx = iter + 1;

                  spectrumList.removeAt(thisIdx);
                  spectrumList.insert(nextIdx, thisSpectrum);

                  changed = true;

                  continue;
                }

              if(thisFirstPoint->x() == nextFirstPoint->x() &&
                 thisLastPoint->x() > nextLastPoint->x())
                {
                  // Reverse the spectra in the list, as this spectrum
                  // is longer than is the next spectrum.

                  int thisIdx = iter;
                  int nextIdx = iter + 1;

                  spectrumList.removeAt(thisIdx);
                  spectrumList.insert(nextIdx, thisSpectrum);

                  changed = true;

                  continue;
                }
            }
          // End of
          // for(int iter = 0; iter < spectrumList.size() + 1; ++iter)
          // That is done making one iteration in the spectrum list.

          // If changed is true, then that means that we still have at
          // least one round to do to finalize the sorting.

          // If changed is false, then the spectrum list is sorted !

          sorted = !changed;
        }
      // End of
      // while(!sorted)
      //
      // That is, finished sorting of the spectrum list.
    }
  // End of
  // if(spectrumList.size() >= 2)

  m_ui.logPlainTextEdit->textCursor().insertText(" Done.\n");
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  QApplication::processEvents();

  // for(int iter = 0; iter < spectrumList.size(); ++iter)
  //   {
  //     QList<QPointF *> *spectrum = spectrumList.at(iter);

  //     QPointF *firstPoint = spectrum->first();
  //     QPointF *lastPoint = spectrum->last();

  //     qDebug() << __FILE__ << __LINE__
  //              << "List of sorted spectra:\n";

  //     qDebug()
  //       << "Spectrum index:" << iter
  //       << "spectrum: " << spectrum
  //       << "first point:" << firstPoint->x()
  //       << "last point:" << lastPoint->x();
  //   }

  m_ui.logPlainTextEdit->appendPlainText(
    tr("Computing sets of overlapping/non-overlapping "
       "sub-spectra..."));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  // Compute sets of overlapping spectra or of a single spectra
  // non-overlapping spectrum, without loosing the sorting made
  // before. One set of spectra is thus a list of lists of QPointF
  // instances.
  // To make things clear:
  //
  // A spectrum: QList<QPointF *>
  // A set of spectra: QList<QList<QPointF *> *>
  // A list of set of spectra: Qlist<QList<QList<QPointF *> *> *>
  QList<QList<QList<QPointF *> *> *> spectrumSetList;

  // Seed the iterations in the spectrum list at 0

  // During one iteration, all the members of the spectrum list are
  // tested to check if there is an overlap between these iterated
  // members and the seeded spectrum. Each time an overlap is found,
  // the new range border points are stored for the next test.

  // qDebug() << __FILE__ << __LINE__
  //          << "Before making spectral sets"
  //          << "spectrumList size:" << spectrumList.size();

  //  for(int iter = 0 ; iter < spectrumList.size() - 1; ++iter)
  while(!spectrumList.isEmpty())
    {
      QList<QPointF *> *thisSpectrum = spectrumList.first();

      // Allocate a new set of spectra that will hold all the spectra
      // that are overlapping with thisSpectrum.

      QList<QList<QPointF *> *> *newSpectrumSet = new QList<QList<QPointF *> *>;

      // Transer right away thisSpectrum to the new set of overlapping
      // spectra (if it remains alone because no spectrum overlaps
      // with it, then fine.

      spectrumList.removeOne(thisSpectrum);
      newSpectrumSet->append(thisSpectrum);
      spectrumSetList.append(newSpectrumSet);

      // If there are overlaps, we'll have to document them using new
      // 'borders' of the current spectrum set. We seed these border's
      // points with thisSpectrum's first and last points.

      // double newRangeFirstPointX = thisSpectrum->first()->x();
      double newRangeLastPointX = thisSpectrum->last()->x();

      // qDebug() << __FILE__ << __LINE__
      //          << "\nNew set seeded with thisSpectrum:" << thisSpectrum
      //          << "\n\t with last point x:" << thisSpectrum->last()->x()
      //          << "\n\t and newRangeLastPointX:" << newRangeLastPointX;

      // Now iterate in each remaining spectrum and check the overlap
      // with thisSpectrum. Remark that we can start at jter = 0
      // because we already removed the seeding spectrum for the list.

      for(int jter = 0; jter < spectrumList.size(); ++jter)
        {
          QList<QPointF *> *nextSpectrum = spectrumList.at(jter);

          double nextFirstPointX = nextSpectrum->first()->x();
          // double nextLastPointX = nextSpectrum->last()->x();

          // Check if there is an overlap of thisSpectrum with
          // nextSpectrum. Because we have sorted the spectra in a
          // previous step, thisSpectrum is necessarily <=
          // nextSpectrum. So, make the tests accordingly.

          if(nextFirstPointX < newRangeLastPointX)
            {
              // There is an overlap, as next spectrum has its first
              // point located inside the first spectrum.

              // That means that we have to move nextSpectrum to the
              // set of overlapping spectra.

              spectrumList.removeOne(nextSpectrum);
              newSpectrumSet->append(nextSpectrum);

              // Update the new right border of the range.

              newRangeLastPointX = nextSpectrum->last()->x();

              // We removed a spectrum from the list, thus decrement
              // jter so as to start a new loop at what was the next
              // spectrum in the list prior to the removal.
              --jter;
            }
          else
            {
              // There is no overlap between the last processed
              // spectrum, thus we break, which will "close" this
              // current set and start a new one.

              break;
            }
        }
      // End of
      // for(int jter = 0 ; jter < spectrumList.size(); ++jter)
    }
  // End of
  // for(int iter = 0 ; iter < spectrumList.size() + 1; ++iter)

  // If there was a last item in the spectrumList, it is necessarily a
  // non-overlapping item, thus allocate a last spectrumSet and
  // append the item to it.

  if(spectrumList.size())
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  m_ui.logPlainTextEdit->textCursor().insertText(" Done.\n\n");
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  // At this point we have a list of spectrum sets which each might
  // contain one or more spectra. When more than one spectra are
  // contained in a spectrum set, then that means that they overlap
  // with each other.

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("Creating final spectrum... "));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  // Iterate in each spectrum set and see how to create the final
  // spectrum.

  QList<QPointF *> finalSpectrum;

  double pointCount = 0;

  for(int iter = 0; iter < spectrumSetList.size(); ++iter)
    {
      QList<QList<QPointF *> *> *set = spectrumSetList.at(iter);

      // Only iterate in the spectrum set if it contains overlapping
      // spectra, that is more than one single spectrum. Otherwise
      // just dump all the points of the non-overlapping spectrum into
      // the final spectrum.

      if(set->size() == 1)
        {
          // Only one non-overlapping spectrum in the set, dump all
          // its points into the final spectrum.

          while(!set->first()->isEmpty())
            {
              finalSpectrum.append(set->first()->takeFirst());

              ++pointCount;
              if(pointCount >= 1000)
                {
                  m_ui.logPlainTextEdit->textCursor().insertText(". ");
                  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
                  qApp->processEvents();

                  pointCount = 0;

                  qApp->processEvents();
                }
            }

          continue;
        }

      // There are more than one spectrum in the set, that is these
      // spectra are overlapping. We have to ensure we sum all the
      // spectra one with the other. minMz is the first point of the
      // first spectrum of the set, while maxMz is the last point of
      // the last spectrum of the set.

      double minMz = set->first()->first()->x();
      double maxMz = set->last()->last()->x();

      double curMz = minMz;

      while(curMz <= maxMz)
        {
          // qDebug() << __FILE__ << __LINE__
          //          << "Handling mz value:" << curMz;

          // curMz now has a mz value for which we have to sum all the
          // intensities in each pointList.

          double summedIntensity = 0;

          // Iterate in each spectrum of the set of spectra and seek
          // the intensity at curMz. when found, add that intensity to
          // summedIntensity.

          double increment = 0;

          for(int jter = 0, jSize = set->size(); jter < jSize; ++jter)
            {
              QList<QPointF *> *curSpectrum = set->at(jter);

              increment = incrementHash.value(curSpectrum);

              // We are iterating in the list of points of a
              // spectrum. What is the intensity of the point at
              // x=curMz ? We provide a tolerance for x corresponding
              // to (increment / 2).

              for(int kter = 0, kSize = curSpectrum->size(); kter < kSize;
                  ++kter)
                {
                  QPointF *point = curSpectrum->at(kter);

                  double mz = point->x();

                  if(mz <= (curMz + increment / 2) &&
                     mz >= (curMz - increment / 2))
                    {
                      summedIntensity += point->y();

                      break;
                    }
                  // Else, go on to next point. If not point is
                  // found at all, then, ok, we do not increment the
                  // summedIntensity variable.
                }
              // At this point we have finished iterating in a given
              // spectrum (that is a point list).
            }
          // We have finished iterating in all the spectra of the set
          // for the current mz. Make a point (curMz,summedIntensity).

          QPointF *newPoint = new QPointF(curMz, summedIntensity);

          finalSpectrum.append(newPoint);

          ++pointCount;
          if(pointCount >= 1000)
            {
              m_ui.logPlainTextEdit->textCursor().insertText(". ");
              // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
              qApp->processEvents();

              pointCount = 0;

              if(m_aborted)
                break;
            }

          // qDebug() << __FILE__ << __LINE__
          //          << "Added new point:"
          //          << "x:" << newPoint->x() << "," << "y:" << newPoint->y()
          //          << "\n";

          curMz += increment;
        }
      // End of while(curMz <= maxMz). That is, we have finished
      // creating (x,y) pairs for the whole x axis, that is the mz
      // ratio axis.
    }
  // End of
  // for(int iter = 0; iter < spectrumSetList.size(); ++iter)
  //
  // We have finished iterating in the set of spectra, that is we have
  // finished going through all the spectra, that is, we have finished
  // doing the final spectrum.

  // qDebug() << __FILE__ << __LINE__
  //          << "Ended the creation of the final spectrum.";

  m_ui.logPlainTextEdit->textCursor().insertText(
    "\n\nDone creating the spectrum.\n\n");
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  // Finally, if the user had asked for the data to be sent to a
  // file... or to a string... Send the spectrum data either to the
  // file or to the text edit widget.

  QString dataString;

  if(!m_filePath.isEmpty())
    {
      // Send all the data (that is, the points located in
      // QList<QPointF *>m_patternPointList) to the output file.

      QFile file(m_filePath);

      if(!file.open(QFile::WriteOnly | QFile::Truncate))
        {
          QMessageBox::warning(this,
                               tr("massXpert - Spectrum Calculator"),
                               tr("Failed to export the isotopic pattern "
                                  "data to file."),
                               QMessageBox::Ok);
          return;
        }

      QTextStream stream(&file);

      m_ui.logPlainTextEdit->textCursor().insertText(
        "Writing data to file... ");
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      for(int iter = 0, size = finalSpectrum.size(); iter < size; ++iter)
        {
          QPointF *point = finalSpectrum.at(iter);

          dataString += QString().setNum(point->x(), 'f', 10);
          dataString += " ";
          dataString += QString().setNum(point->y(), 'f', 10);
          dataString += "\n";

          // Stream the data by packets of 4096 or less characters.
          if(dataString.size() > 4096)
            {
              stream << dataString;
              dataString.clear();

              m_ui.logPlainTextEdit->textCursor().insertText(". ");
              // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
              qApp->processEvents();

              if(m_aborted)
                break;
            }
          // qDebug() << __FILE__ << __LINE__ << dataString;
        }

      // One last time:
      stream << dataString;
      dataString.clear();

      // And finally:
      stream.flush();
      file.close();

      m_ui.logPlainTextEdit->textCursor().insertText(
        "\n\nDone writing "
        "data to file.\n\n");
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      return;
    }
  else
    {
      m_ui.logPlainTextEdit->textCursor().insertText(
        "Writing data to results tab... ");
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      for(int iter = 0, size = finalSpectrum.size(); iter < size; ++iter)
        {
          QPointF *point = finalSpectrum.at(iter);

          dataString += QString().setNum(point->x(), 'f', 10);
          dataString += " ";
          dataString += QString().setNum(point->y(), 'f', 10);
          dataString += "\n";

          // Append the data by packets of 4096 or less characters.
          if(dataString.size() > 4096)
            {
              m_ui.resultPlainTextEdit->appendPlainText(dataString);
              dataString.clear();

              m_ui.logPlainTextEdit->textCursor().insertText(". ");
              // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
              qApp->processEvents();

              if(m_aborted)
                break;
            }
        }

      // One last time:
      m_ui.resultPlainTextEdit->appendPlainText(dataString);
      dataString.clear();

      m_ui.logPlainTextEdit->textCursor().insertText(
        "\n\nDone writing "
        "data to results tab.\n\n");
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();
    }


  // Finally, we have a bunch of data to free.

  QPointF *point = 0;

  // Free all the spectrum sets along with all the QPointF instances
  // contained in their spectra.
  while(!spectrumSetList.isEmpty())
    {
      QList<QList<QPointF *> *> *spectrumList = spectrumSetList.takeFirst();

      for(int iter = 0, size = spectrumList->size(); iter < size; ++iter)
        {
          QList<QPointF *> *curSpectrum = spectrumList->at(iter);

          foreach(point, *curSpectrum)
            delete point;

          curSpectrum->clear();

          delete curSpectrum;
        }

      delete spectrumList;
    }

  // qDebug() << __FILE__ << __LINE__
  //          << "Done freeing up material.";

  foreach(point, finalSpectrum)
    {
      // qDebug() << point->x() << " " << point->y();

      delete point;
    }

  finalSpectrum.clear();
}


void
SpectrumCalculationDlg::executeSpectrumWithCluster(
  QList<QList<QPointF *> *> *spectrumList,
  QHash<QList<QPointF *> *, double> *incrementHash)
{
  if(!spectrumList)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  if(!incrementHash)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("Simulating a spectrum "
       "with calculation of an isotopic "
       "cluster for each oligomer.\n\n"));

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("There are %1 oligomers\n"
       "Calculating sub-spectrum "
       "for each\n\n")
      .arg(mp_oligomerList->size()));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  for(int iter = 0, size = mp_oligomerList->size(); iter < size; ++iter)
    {
      Oligomer *oligomer = mp_oligomerList->at(iter);

      // 1. Does the user want a isotopic cluster calculation for
      // each oligomer in the list? If so we have to manage a list
      // of IsotopicPatternCalculator instances.

      // Since we have to perform an isotopic cluster
      // calculation, retrieve the elemental composition.
      StringProp *prop =
        static_cast<StringProp *>(oligomer->prop("ELEMENTAL_COMPOSITION"));

      if(!prop)
        {
          // qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

          QMessageBox::warning(this,
                               tr("massXpert - Spectrum Calculator"),
                               tr("Elemental composition not found for "
                                  "current oligomer.\n Uncheck the Isotopic "
                                  "cluster checkbox for this calculation."),
                               QMessageBox::Ok);

          return;
        }

      QString *formulaString = static_cast<QString *>(prop->data());

      msXpSlibmass::Formula formula(*formulaString);

      m_ui.logPlainTextEdit->textCursor().insertText(
        tr("Computing isotopic cluster"
           " for oligomer %1\n"
           "\tformula: %2.\n")
          .arg(iter + 1)
          .arg(*formulaString));

      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      // We want to validate the formula and in the mean time
      // construct the list of all the AtomCount objects(first
      // param true), and since the formula is never reused we
      // do not need to reset (second param false). m_atomList
      // is a reference to the atom list of the polymer
      // chemistry definition currently used in the caller
      // calculator window.

      m_ui.logPlainTextEdit->textCursor().insertText(
        tr(" Validating formula... "));
      qApp->processEvents();

      if(!formula.validate(m_atomList, true, false))
        {
          m_ui.inputDataFeedbackLineEdit->setText(tr("Formula error"));

          m_ui.logPlainTextEdit->textCursor().insertText(tr(" Failure.\n\n"));
          // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
          qApp->processEvents();

          return;
        }

      m_ui.logPlainTextEdit->textCursor().insertText(tr("Success.\n"));
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      double mono   = oligomer->mono();
      double charge = oligomer->charge();

      // Because we have the mono m/z, we can compute the fwhm:
      double fwhm = (mono / m_resolution);
      m_config.setFwhm(fwhm);

      double increment =
        (msXpS::FWHM_PEAK_SPAN_FACTOR * fwhm) / m_config.pointNumber();

      // But we have to take into account the charge:
      if(charge)
        increment = increment / charge;

      m_config.setIncrement(increment);

      m_ui.logPlainTextEdit->textCursor().insertText(tr("\tmono m/z: %1\n"
                                                        "\tcharge: %2\n"
                                                        "\tfwhm: %3\n"
                                                        "\tincrement: %4\n\n")
                                                       .arg(mono)
                                                       .arg(charge)
                                                       .arg(fwhm)
                                                       .arg(increment));
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      // qDebug() << __FILE__ << __LINE__
      //          << "index:" << iter << "\n"
      //          << "formula: " << formula.formula() << "\n"
      //          << "charge:" << charge << "\n"
      //          << "mono m/z:" << mono << "\n"
      //          << "avg m/z:" << oligomer->avg() << "\n"
      //          << "resolution:" << m_resolution << "\n"
      //          << "fwhm:" << fwhm << "\n"
      //          << "Whole config:" << m_config.config();

      msXpSlibmass::IsotopicPatternCalculator *calculator =
        new msXpSlibmass::IsotopicPatternCalculator(formula,
                                                    charge,
                                                    m_maximumPeaks,
                                                    m_minimumProbability,
                                                    m_atomList,
                                                    m_config);

      // Perform the calculation...
      const QList<QPointF *> &pointList = calculator->sumPeakShapes();

      if(pointList.isEmpty())
        qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);

      QList<QPointF *> *newPointList = new QList<QPointF *>;

      calculator->transferPoints(newPointList);

      spectrumList->append(newPointList);

      m_ui.logPlainTextEdit->textCursor().insertText(
        tr("\t\tDone computing the cluster\n\n"));
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      // Store along with the point list, the increment that was
      // used for that calculation. We'll need it later when
      // computing the whole spectrum.

      incrementHash->insert(newPointList, m_config.increment());

      // At this point we have the points, we can delete the
      // calculator.
      delete calculator;

      if(m_aborted)
        break;
    }
  // End of
  // for(int iter = 0,
  //         size = mp_oligomerList->size(); iter < size; ++iter)

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("Done computing *all* the clusters\n\n"));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();
}


void
SpectrumCalculationDlg::executeSpectrumWithoutCluster(
  QList<QList<QPointF *> *> *spectrumList,
  QHash<QList<QPointF *> *, double> *incrementHash)
{
  if(!spectrumList)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  if(!incrementHash)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("Simulating a spectrum "
       "without calculation of isotopic "
       "clusters for each "
       "oligomer.\n\n"));

  m_ui.logPlainTextEdit->appendPlainText(tr("There are %1 oligomers\n"
                                            "Calculating sub-spectrum "
                                            "for each\n\n")
                                           .arg(mp_oligomerList->size()));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();

  for(int iter = 0, size = mp_oligomerList->size(); iter < size; ++iter)
    {
      Oligomer *oligomer = mp_oligomerList->at(iter);

      // Not asking for an isotopic cluster to be computed for
      // each oligomer. The user want the mono or avg mass of
      // the oligomer to be considered the centroid of a peak
      //
      double mz = 0;

      if(m_withMonoMass)
        mz = oligomer->mono();
      else
        mz = oligomer->avg();

      double charge = oligomer->charge();

      msXpSlibmass::PeakCentroid centroid(mz, 100, 1);

      // Because we have the m/z, we can compute the fwhm:
      double fwhm = (mz / m_resolution);
      m_config.setFwhm(fwhm);

      double increment =
        (msXpS::FWHM_PEAK_SPAN_FACTOR * fwhm) / m_config.pointNumber();

      // But we have to take into account the charge:
      if(charge)
        increment = increment / charge;

      m_config.setIncrement(increment);

      m_ui.logPlainTextEdit->textCursor().insertText(
        tr("Computing peak shape for oligomer %1\n").arg(iter + 1));

      if(m_withMonoMass)
        m_ui.logPlainTextEdit->textCursor().insertText(
          tr("\tmono m/z: %1\n").arg(mz));
      else
        m_ui.logPlainTextEdit->textCursor().insertText(
          tr("\tavg m/z: %1\n").arg(mz));

      m_ui.logPlainTextEdit->textCursor().insertText(tr("\tcharge: %2\n"
                                                        "\tfwhm: %3\n"
                                                        "\tincrement: %4\n\n")
                                                       .arg(charge)
                                                       .arg(fwhm)
                                                       .arg(increment));
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      // qDebug() << __FILE__ << __LINE__
      //          << "index:" << iter << "\n"
      //          << "charge:" << charge << "\n"
      //          << "m/z:" << mz << "\n"
      //          << "avg m/z:" << oligomer->avg() << "\n"
      //          << "resolution:" << m_resolution << "\n"
      //          << "fwhm:" << fwhm << "\n"
      //          << "Whole config:" << m_config.config();

      msXpSlibmass::PeakShape *shape =
        new msXpSlibmass::PeakShape(centroid, m_config);

      shape->calculatePeakShape();

      const QList<QPointF *> &pointList = shape->pointList();

      if(pointList.isEmpty())
        qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);

      QList<QPointF *> *newPointList = new QList<QPointF *>;

      shape->transferPoints(newPointList);

      spectrumList->append(newPointList);

      m_ui.logPlainTextEdit->textCursor().insertText(
        tr("\t\tDone computing the peak shape\n\n"));
      // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
      qApp->processEvents();

      // Store along with the point list, the increment that was
      // used for that calculation. We'll need it later when
      // computing the whole spectrum.

      incrementHash->insert(newPointList, m_config.increment());

      // At this point we have the points, we can delete the
      // peack shape.
      delete shape;

      if(m_aborted)
        break;
    }
  // End of
  // for(int iter = 0,
  //         size = mp_oligomerList->size(); iter < size; ++iter)

  m_ui.logPlainTextEdit->textCursor().insertText(
    tr("Done computing *all* the peak shapes\n\n"));
  // m_ui.logPlainTextEdit->moveCursor(QTextCursor::Down);
  qApp->processEvents();
}


void
SpectrumCalculationDlg::spectrumCalculationProgressValueChanged(int value)
{
  m_ui.progressBar->setValue(value);
  // qDebug() << __FILE__ << __LINE__
  //          << "spectrumCalculationProgressValueChanged:" << value;
  qApp->processEvents();
}


void
SpectrumCalculationDlg::spectrumCalculationMessageChanged(QString text)
{
  m_ui.logPlainTextEdit->appendPlainText(text);
  // qDebug() << __FILE__ << __LINE__
  //          << "spectrumCalculationMessageChanged:" << text;
  qApp->processEvents();
}


void
SpectrumCalculationDlg::abort()
{
  m_aborted = true;
  emit(spectrumCalculationAborted());
}


void
SpectrumCalculationDlg::outputFile()
{
  QString name;

  m_filePath = QFileDialog::getSaveFileName(
    this, tr("Export Raw Text File"), QDir::homePath(), tr("Any file type(*)"));
}

void
SpectrumCalculationDlg::debugPutStdErrBitset(QString file,
                                             int line,
                                             int value,
                                             const QString &text)
{
  qDebug() << file << line << "\n"
           << msXpS::binaryRepresentation(VALIDATION_ERRORS_NONE)
           << ": VALIDATION_ERRORS_NONE"
           << "\n"
           << msXpS::binaryRepresentation(VALIDATION_FORMULA_ERRORS)
           << ": VALIDATION_FORMULA_ERRORS"
           << "\n"
           << msXpS::binaryRepresentation(VALIDATION_RESOLUTION_ERRORS)
           << ": VALIDATION_RESOLUTION_ERRORS"
           << "\n"
           << msXpS::binaryRepresentation(VALIDATION_FWHM_ERRORS)
           << ": VALIDATION_FWHM_ERRORS"
           << "\n"
           << "\n"
           << msXpS::binaryRepresentation(value) << ":" << text << ":" << value
           << "\n";
}

} // namespace msXpSmassXpert


#if 0
/***************************************************************************
 *  file                 :  ipc.c                                          *
 *  copyright            :(C) 2001-2005 by Dirk Nolting                   *
 *  email                : nolting@uni-duesseldorf.de                      *
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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~2 of the License, or     *
 *  (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <global.h>
#include <ipc.h>
#include <pars.h>
#include <element.h>
#include <gp_out.h>
#include <time.h>
#include <signal.h>
#include <math.h>

//#define SUMMARIZE_LEVEL 0.00000001
//#define SUMMARIZE_LEVEL 0.0001
//#define SUMMARIZE_LEVEL 0.001
#define SUMMARIZE_LEVEL 0.01

compound *verbindung=NULL;
isotope *m_peakList;
int fast_calc=0;

void free_list(isotope *target)
{
	while(target->next)
	{
		target=target->next;
		free(target->previous);
	}
	free(target);
}

void cut_peaks(isotope *spectrum)
{
	int dummy=1;

	while((spectrum->next) &&(dummy<fast_calc) )
	{
		++dummy;
		spectrum=spectrum->next;
	}

	if(spectrum->next)
	{
		free_list(spectrum->next);
		spectrum->next=NULL;
	}
}

void summarize_peaks()
{  isotope *dummy,*d2;

	for(dummy=m_peakList;dummy;dummy=dummy->next)
		/* Differenz wegen Rundungsfehlern */
		while( dummy->next &&(dummy->next->mass - dummy->mass < SUMMARIZE_LEVEL) )
		{
			d2=dummy->next;
			dummy->next=d2->next;
			if(dummy->next)
				dummy->next->previous=dummy;
			dummy->p+=d2->p;
			free(d2);
		}
}

isotope *add_peak(isotope *base,isotope *peak)
{
	static isotope *IsotopeIterator;

	if(!(base->mass))
	{
		peak->next=NULL;
		peak->previous=NULL;
		IsotopeIterator = peak;
		return peak;
	}

	if( peak->mass >= IsotopeIterator->mass )
		while((IsotopeIterator->next) &&(IsotopeIterator->mass < peak->mass))
			IsotopeIterator=IsotopeIterator->next;
	else
	{
		while((IsotopeIterator->previous) &&(IsotopeIterator->mass > peak->mass) )
			IsotopeIterator=IsotopeIterator->previous;
		IsotopeIterator=IsotopeIterator->next;
	}

	if((IsotopeIterator->mass) >=(peak->mass) )
	{
		peak->next=IsotopeIterator;
		peak->previous=IsotopeIterator->previous;
		peak->previous->next=peak;
		IsotopeIterator->previous=peak;
		return base;
	}
	else
	{
		IsotopeIterator->next=peak;
		peak->next=NULL;
		peak->previous=IsotopeIterator;
		return base;
	}
	return 0;
}

int calculate_peaks(){
	compound *c;
	isotope *newPeakList,*p,*i,*np1;
	int amount;

	if(!(m_peakList=malloc(sizeof(isotope))))
		return 0;
	m_peakList->mass=0;
	m_peakList->p=1;
	m_peakList->previous=NULL;
	m_peakList->next=NULL;

	for (c = verbindung; c; c = c->next)
	{
		for(amount = 0; amount < c->amount; ++amount)
		{
			if (!(newPeakList=malloc(sizeof(isotope))))
				return 0;

			newPeakList->mass = 0;

			for (p = m_peakList; p; p = p->next)
			{
				for(i = c->isotopes; i; i = i->next)
				{
					//printf("working on isotope of mass %f\n", i->mass);

					if (!(np1 = malloc(sizeof(isotope))))
						return 0;

					np1->mass=p->mass + i->mass;
					np1->p=p->p * i->p;

					if(!(newPeakList = add_peak(newPeakList,np1)))
						return 0;
				}
			}

			free_list(m_peakList);
			m_peakList = newPeakList;
			summarize_peaks();

			if (fast_calc)
				cut_peaks(m_peakList);
		}
	}

	return 1;
}


void print_result(int digits,int charge){
	isotope *d;
	double maxp=0,relint=0,sump=0;
	int permutationen=0;

	printf("\n");

	for(d=m_peakList;d;d=d->next)
	{
		++permutationen;
		sump+=d->p;
		d->mass=d->mass / charge;
		d->mass=(rint( d->mass * pow(10,digits) ) / pow(10,digits) );
	}

	summarize_peaks();
	for(d=m_peakList;d;d=d->next)
		if(d->p > maxp)
			maxp=d->p;

	for(d=m_peakList;d;d=d->next)
	{
		if(( relint=(d->p/maxp)*100) > MIN_INT )
			printf("M= %f, p= %e, rel. Int.= %f%%\n",
					d->mass,d->p,relint);
	}
	if(!(fast_calc))
		printf("\nNumber of  permutations: %i\n",permutationen);
	else
	{
		sump=(rint(sump*10000)/100);
		printf("\nCovered Intensity: %2.2f%% \n",sump);
	}
}

int main(int argc,char **argv){
	long seconds;
	int d=1,zeig_summenformel=0,calc_peaks=1,gnuplot=0,use_digits=USE_DIGITS,charge=1;
	char *gnuplotfile=NULL;

	if(!argv[d])
	{
		usage();
		return 1;
	}

#ifdef HAVE_SIGNAL
	signal(SIGHUP,SIG_IGN);
#endif

	if(!init_elements()){
		printf("Error in init_elements\n");
		return 1;
	}
	while(argv[d])
	{
		if(!strcmp(argv[d],"-f"))
		{
			++d;
			if(!argv[d])
			{
				printf("Missing argument for -f\n");
				return 1;
			}
			fast_calc=strtol(argv[d],NULL,10);
		}

		else if(!strcmp(argv[d],"-z"))
		{
			++d;
			if(!argv[d])
			{
				printf("Missing argument for -z\n");
				return 1;
			}
			charge=strtol(argv[d],NULL,10);
		}

		else if(!strcmp(argv[d],"-d"))
		{
			++d;
			if(!argv[d])
			{
				printf("Missing argument for -d\n");
				return 1;
			}
			use_digits=strtol(argv[d],NULL,10);
		}

		else if(!strcmp(argv[d],"-c")){
			++d;
			if(!argv[d])
			{
				printf("Missing argument for -c\n");
				return 1;
			}
			if(!pars_chem_form(argv[d])){
				printf("Parser error.\n");
				return 1;
			}
		}

		else if(!strcmp(argv[d],"-g")){
			++d;
			if(!argv[d]){
				printf("Missing argument for -g\n");
				return 1;
			}
			gnuplot=1;
			if(!(gnuplotfile=strdup(argv[d]))){
				printf("Not enough memory\n");
				return 1;
			}
		}


		else if(!strcmp(argv[d],"-p")){
			++d;
			if(!argv[d]){
				printf("Missing argument for -p\n");
				return 1;
			}
			if(!(pars_peptid(argv[d]))){
				printf("Error in peptid parser.\n");
				return 1;
			}
		}

		else if(!strcmp(argv[d],"-a")){
			++d;
			if(!argv[d]){
				printf("Missing argument for -a\n");
				return 1;
			}
			if(!pars_amino_acid(argv[d])){
				printf("Error in pars_amino_acid.\n");
				return 1;
			}
		}

		else if(!strcmp(argv[d],"-s"))
			zeig_summenformel=1;

		else if(!strcmp(argv[d],"-h"))
		{
			usage();
			return 1;
		}

		else if(!strcmp(argv[d],"-x"))
			calc_peaks=0;

		else
		{
			printf("Unknown flag: %s\n",argv[d]);
			usage();
			return 1;
		}
		++d;
	}

	if(zeig_summenformel)
	{
		if(!print_sum())
		{
			printf("error while showing chemical formula.\n");
			return 1;
		}
	}
#ifdef HAVE_TIME
	seconds=time(0);
#endif

	if(calc_peaks)
		if(!calculate_peaks()){
			printf("Error in calculate_peaks\n");
			return 1;
		}

	print_result(use_digits,charge);

#ifdef HAVE_TIME
	seconds=time(0)-seconds;
	printf("Computing time: %li seconds.\n",seconds);
#endif

	if(gnuplot)
		if(!(make_gnuplot_output(gnuplotfile))){
			printf("Error while generating gnuplot file\n");
			return 1;
		}

	return 0;
}


void usage()
{
	printf("\nThis is IPC v %s\nCopyright Dirk Nolting 2001-2005\n\n",VERSION);
	printf("\nSynopsis:\n ipc -d <int> -z <int> -f <int> <-a <amino acid> -c <chemical formula> -p <File> -g <name> -s -x -h\n\n");

	printf("-c <chemical formula> calculates the isotopic pattern of the \n");
	printf("   given chemical formula. Additive with -p\n");

	printf("-p <File> reads peptide sequenz(one letter notation) from the \n");
	printf("   given file and calculates the isotopic pattern. Additive with -c\n");

	printf("-a <amino acids> calculate isotopic pattern from given amino acids\n");
	printf("   in one letter notation\n");

	printf("-s gives the chemical formula. Usefull for -a or -p\n");
	printf("-g creates gnuplot output and stores it in the file <name> and <name>.gnu\n");

	printf("-x no calculation of the isotopic pattern. Usefull for -s\n");
	printf("-f fast calc, calculates only first <int> peaks\n");
	printf("-d <int> digits significant\n");
	printf("-z assume <int> charges on ion \n");
	printf("-h show this text\n\n");
}
#endif
