/* 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 "PeakShaperDlg.hpp"
#include "../../libmass/MassSpectrum.hpp"
#include "MainWindow.hpp"


namespace msXpSmineXpert
{

PeakShaperDlg::PeakShaperDlg(QWidget *parent, const QString &applicationName)
  : QDialog{parent}, m_applicationName{applicationName}
{
  if(!parent)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  setWindowTitle(QString("%1 - Peak shaper (Gaussian or Lorentzian)")
                   .arg(m_applicationName));

  m_ui.setupUi(this);

  // We want to destroy the dialog when it is closed.
  setAttribute(Qt::WA_DeleteOnClose);

  setupDialog();
}


void
PeakShaperDlg::closeEvent(QCloseEvent *event)
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";
  writeSettings();
}


PeakShaperDlg::~PeakShaperDlg()
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  while(m_peakShapers.size())
    delete m_peakShapers.takeFirst();

  writeSettings();
}


//! Save the settings to later restore the window in its same position.
void
PeakShaperDlg::writeSettings()
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  QSettings settings;
  settings.beginGroup("PeakShaperDlg");
  settings.setValue("geometry", saveGeometry());
  settings.setValue("pointCount", m_ui.pointCountSpinBox->value());
  settings.setValue("resolution", m_ui.resolutionSpinBox->value());
  settings.setValue("charge", m_ui.ionChargeSpinBox->value());
  settings.setValue("fwhm", m_ui.fwhmDoubleSpinBox->value());
  settings.setValue("binSize", m_ui.binSizeDoubleSpinBox->value());
  settings.setValue("splitter", m_ui.splitter->saveState());
  settings.endGroup();
}


//! Read the settings to restore the window in its last position.
void
PeakShaperDlg::readSettings()
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  QSettings settings;
  settings.beginGroup("PeakShaperDlg");
  restoreGeometry(settings.value("geometry").toByteArray());
  m_ui.pointCountSpinBox->setValue(settings.value("pointCount", 150).toInt());
  m_ui.resolutionSpinBox->setValue(settings.value("resolution", 45000).toInt());
  m_ui.ionChargeSpinBox->setValue(settings.value("charge", 1).toInt());
  m_ui.fwhmDoubleSpinBox->setValue(settings.value("fwhm", 0).toDouble());
  m_ui.binSizeDoubleSpinBox->setValue(settings.value("binSize", 0).toDouble());
  m_ui.splitter->restoreState(settings.value("splitter").toByteArray());
  settings.endGroup();
}


void
PeakShaperDlg::setupDialog()
{
  // Ranges for various numerical values
  m_ui.resolutionSpinBox->setRange(0, 1000000);
  m_ui.fwhmDoubleSpinBox->setRange(0, 10);
  m_ui.pointCountSpinBox->setRange(5, 500);
  m_ui.ionChargeSpinBox->setRange(1, 10000);

  // The values above are actually set in readSettings (with default values if
  // missing in the config settings.)
  readSettings();

  // Always start the dialog with the first page of the tab widget,
  // the input data page.
  m_ui.tabWidget->setCurrentIndex(static_cast<int>(TabWidgetPage::INPUT_DATA));

  // By default we want a gaussian-type shape.
  m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::GAUSSIAN);
  m_ui.gaussianRadioButton->setChecked(true);

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

  // Get the number of points used to craft the peak curve.
  m_config.setPointCount(m_ui.pointCountSpinBox->value());

  connect(m_ui.resolutionSpinBox,
          static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
          this,
          &PeakShaperDlg::resolutionChanged);

  connect(m_ui.fwhmDoubleSpinBox,
          static_cast<void (QDoubleSpinBox::*)(double)>(
            &QDoubleSpinBox::valueChanged),
          this,
          &PeakShaperDlg::fwhmChanged);

  connect(m_ui.pointCountSpinBox,
          static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
          this,
          &PeakShaperDlg::pointCountChanged);

  connect(m_ui.gaussianRadioButton,
          &QRadioButton::toggled,
          this,
          &PeakShaperDlg::gaussianRadioButtonToggled);

  connect(m_ui.lorentzianRadioButton,
          &QRadioButton::toggled,
          this,
          &PeakShaperDlg::lorentzianRadioButtonToggled);

  connect(m_ui.runPushButton, &QPushButton::clicked, this, &PeakShaperDlg::run);

  connect(m_ui.outputFilePushButton,
          &QPushButton::clicked,
          this,
          &PeakShaperDlg::outputFileName);

  connect(m_ui.displayMassSpectrumPushButton,
          &QPushButton::clicked,
          this,
          &PeakShaperDlg::displayMassSpectrum);

  connect(
    m_ui.toFwhmPushButton, &QPushButton::clicked, this, &PeakShaperDlg::toFwhm);


  connect(m_ui.sortDataPointsPushButton,
          &QPushButton::clicked,
          this,
          &PeakShaperDlg::sortInputDataPointsTextEdit);
}


QString
PeakShaperDlg::craftMassSpectrumName()
{
  // The user might have forgotten to insert a preferred name in the mass
  // spectrum file name edit widget. We thus craft one on the basis of the
  // centroid peaks and the current time.

  QString mass_spectrum_name =
    QString("%1-%2@%3")
      .arg(m_peakShapers.at(0)->getPeakCentroid().key())
      .arg(m_peakShapers.at(m_peakShapers.size() - 1)->getPeakCentroid().key())
      .arg(QTime::currentTime().toString("hh:mm:ss.zzz"));

  return mass_spectrum_name;
}


std::size_t
PeakShaperDlg::fillInThePeakShapers()
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  // Free all the peak shapers that might have been created in a previous run.
  while(m_peakShapers.size())
    delete m_peakShapers.takeFirst();

  // Sort the lines in the input data points text edit widget such that the
  // first item of the line (mz) will be in increasing order.

  QString input_data_string = m_ui.inputDataPointsPlainTextEdit->toPlainText();
  QStringList input_data_string_list =
    input_data_string.split("\n", QString::SkipEmptyParts);

  QString line;

  foreach(line, input_data_string_list)
    {

      // Check if line empty.
      if(line.isEmpty())
        continue;

      // Check if the line is empty with only a line return.
      if(msXpS::gEndOfLineRegExp.match(line).hasMatch())
        continue;

      msXpSlibmass::DataPoint data_point;

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "Parsing current line:" << line;

      if(!data_point.initialize(line))
        {
          QMessageBox::warning(
            0,
            tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
            QString("Could not interpret data input line %1.").arg(line),
            QMessageBox::Ok);

          return false;
        }

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "DataPoint mz value:" << QString("%1").arg(data_point.key(), 0, 'f',
      // 30);

      // Now that we know that the data point has a correct syntax, we need to
      // apply to it the charge the user wants it to have.
      data_point.setKey(data_point.key() / m_config.getCharge());

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "DataPoint mz value:" << QString("%1").arg(data_point.key(), 0, 'f',
      // 30);

      m_peakShapers.append(new msXpSlibmass::PeakShaper(data_point, m_config));
    }

  // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
  //<< "m_config:" << m_config.asText(800);

  message(
    QString("The number of peak centroids is: %1").arg(m_peakShapers.size()));

  // qDebug() << __FILE__ << "@" << __LINE__ << "EXIT" << __FUNCTION__ << "()";

  return m_peakShapers.size();
}


void
PeakShaperDlg::sortInputDataPointsTextEdit()
{
  QString sorted_data_string = sortInputDataPoints();
  m_ui.inputDataPointsPlainTextEdit->setPlainText(sorted_data_string);
}


msXpSlibmass::DataPoint
PeakShaperDlg::probeFirstInputDataPoint()
{
  // We need to get the first DataPoint from the textual list of (mz,i) pairs.
  // We iterate in the list of lines, because some might be empty. But we return
  // the firt valid data point that is found.

  QString input_data_string = m_ui.inputDataPointsPlainTextEdit->toPlainText();
  QStringList input_data_string_list =
    input_data_string.split("\n", QString::SkipEmptyParts);

  QString line;

  // This is an invalid data point that will be checked.
  msXpSlibmass::DataPoint data_point;

  foreach(line, input_data_string_list)
    {
      // Check if line empty.
      if(line.isEmpty())
        continue;

      // Check if the line is empty with only a line return.
      if(msXpS::gEndOfLineRegExp.match(line).hasMatch())
        continue;

      // If the initialization fails, it returns false but also, the DataPoint
      // is not valid, which can be tested later.
      data_point.initialize(line);

      return data_point;
    }

  // This is an invalid data point that will be checked.
  return data_point;
}


QString
PeakShaperDlg::sortInputDataPoints()
{

  // Sort the lines in the input data points text edit widget such that the
  // first item of the line (mz) will be in increasing order.

  QString input_data_string = m_ui.inputDataPointsPlainTextEdit->toPlainText();
  QStringList input_data_string_list =
    input_data_string.split("\n", QString::SkipEmptyParts);

  // The stratey is to convert each line into a DataPoint object that is
  // appended to a vector. Then, the vector is sorted on the basis of the
  // key()
  // value of each DataPoint instance that it has. This way, we will not only
  // sort the DataPoint (to recreate the sorted string) but we also ensure
  // that
  // all the lines in the input data text widget are correct.

  std::vector<msXpSlibmass::DataPoint> data_points;

  QString line;

  foreach(line, input_data_string_list)
    {
      // Check if line empty.
      if(line.isEmpty())
        continue;

      // Check if the line is empty with only a line return.
      if(msXpS::gEndOfLineRegExp.match(line).hasMatch())
        continue;

      msXpSlibmass::DataPoint data_point;

      if(!data_point.initialize(line))
        {
          QMessageBox::warning(
            0,
            tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
            QString("Could not interpret data input line %1.").arg(line),
            QMessageBox::Ok);

          return QString();
        }

      data_points.push_back(data_point);
    }

  // Now sort the DataPoint instances in the vector.

  std::sort(
    data_points.begin(),
    data_points.end(),
    [](const msXpSlibmass::DataPoint &dp1, const msXpSlibmass::DataPoint &dp2) {
      return dp1.key() < dp2.key();
    });

  // And use these sorted DataPoint instances to recreate the sorted string.
  QString result;

  for(auto &dp : data_points)
    result +=
      QString("%1 %2\n").arg(dp.key(), 0, 'f', 30).arg(dp.val(), 0, 'f', 30);

  return result;
}


bool
PeakShaperDlg::fetchValidateInputData()
{
  // qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  // Remove all the text in the log text edit widget.
  m_ui.logPlainTextEdit->clear();

  // Remove all text in the results text edit widget.
  m_ui.resultPlainTextEdit->clear();

  // Make sure we have some (mz,i) values to crunch.
  QString input_mz_i_pairs = m_ui.inputDataPointsPlainTextEdit->toPlainText();

  if(input_mz_i_pairs.isEmpty())
    {
      message("The list of centroid peaks is empty");

      qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
               << "The list of centroid peaks is empty.";

      return false;
    }

  // Probe the first valid (mz,i) pair from the input data points text edit
  // widget as we'll need its mz value later.

  msXpSlibmass::DataPoint data_point = probeFirstInputDataPoint();

  if(!data_point.isValid())
    {
      message("No valid DataPoint instance was found");

      qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
               << "No valid DataPoint instance was found";

      return false;
    }

  // Get to know if we want gaussian or lorentzian shapes.
  if(m_ui.gaussianRadioButton->isChecked())
    {
      m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::GAUSSIAN);
      m_ui.logPlainTextEdit->appendPlainText("Peak shape is Gaussian\n");
    }
  else
    {
      m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::LORENTZIAN);
      m_ui.logPlainTextEdit->appendPlainText("Peak shape is Lorentzian\n");
    }

  // Now check the resolution/fwhm data that are competing one another.

  double fwhm    = m_ui.fwhmDoubleSpinBox->value();
  int resolution = m_ui.resolutionSpinBox->value();

  if(!resolution && !fwhm)
    {
      QMessageBox::warning(
        0,
        tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
        tr("Please, fix the Resolution / FWHM value (one or the other)"),
        QMessageBox::Ok);

      m_ui.logPlainTextEdit->clear();

      return false;
    }

  // At this point we'll have some things to report to the LOG tab,
  // switch to it now.
  m_ui.tabWidget->setCurrentIndex(static_cast<int>(TabWidgetPage::LOG));

  if(resolution)
    {
      // We will use the resolution
      m_config.setResolution(resolution);

      double mz = data_point.key();

      double fwhm = m_config.fwhm(mz);
      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "Get resolution:" << m_config.resolution(mz)
      //<< "Compute/Get FWHM:" << fwhm;

      m_ui.logPlainTextEdit->appendPlainText(
        QString("Resolution is used to determine the "
                "width of the peak shape: %1.\n"
                "The FWHM is thus (for mz = %2): %3.\n")
          .arg(resolution)
          .arg(data_point.key(), 0, 'f', 5)
          .arg(fwhm));
    }
  else
    {
      // We will use the FWHM
      m_config.setFwhm(fwhm);

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      // double mz = data_point.key();
      //<< "Get resolution:" << m_config.resolution(mz)
      //<< "Compute/Get FWHM:" << m_config.fwhm(mz);

      m_ui.logPlainTextEdit->appendPlainText(
        QString("FWHM is used to determine the "
                "width of the peak shape: %1.\n")
          .arg(fwhm, 0, 'f', 6));

      double binSize = m_ui.binSizeDoubleSpinBox->value();
      if(!binSize)
        m_ui.logPlainTextEdit->appendPlainText(
          QString("The bin size is set to [FWHM / 10]: %1.\n").arg(fwhm / 10));
      else
        m_ui.logPlainTextEdit->appendPlainText(
          QString("The bin size is set to: %1.\n").arg(binSize));
    }

  m_config.setCharge(m_ui.ionChargeSpinBox->value());
  m_config.setPointCount(m_ui.pointCountSpinBox->value());


  // At this point, because we have set all the relevant values to the m_config
  // PeakShapeConfig, we can create the PeakShaper instances and set to them the
  // m_config.

  if(!fillInThePeakShapers())
    {
      QMessageBox::warning(
        0,
        tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
        tr("Please, fix the (mz,i) pairs. Make sure they are in the following "
           "format:\n"
           "<mz value><separator><i value>, with <separator> being any non "
           "numerical character set."),
        QMessageBox::Ok);

      return false;
    }

  m_ui.logPlainTextEdit->appendPlainText(
    QString("Number of input centroid data points to process: %1.\n")
      .arg(m_peakShapers.size()));

  m_ui.logPlainTextEdit->appendPlainText(
    m_config.asText(m_peakShapers.first()->getPeakCentroid().key()));

  // qDebug() << __FILE__ << "@" << __LINE__ << "EXIT" << __FUNCTION__ << "()";

  return true;
}


void
PeakShaperDlg::resolutionChanged(int value)
{
  double resolution = value;

  if(resolution == 0)
    {
      // Tell the user to set a valid FWHM value, then.
      message("Will use the FWHM. Please, set a valid FWHM value");

      m_ui.resolutionSpinBox->setValue(0);

      return;
    }

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

  message("Will use the resolution");

  // Now set the "competing" fwhm value to 0.
  m_ui.fwhmDoubleSpinBox->setValue(0);

  return;
}


void
PeakShaperDlg::fwhmChanged(double value)
{
  double fwhmValue = value;

  if(fwhmValue == 0)
    {
      // Tell the user to set a valid resolution value, then.
      message("Will use the resolution. Please, set a valid resolution value");
      return;
    }

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

  message("Will use the FWHM");

  // Now set the "competing" resolution value to 0.
  m_ui.resolutionSpinBox->setValue(0);

  return;
}


void
PeakShaperDlg::pointCountChanged(int value)
{
  // The points have changed, we should compute the increment.
  m_config.setPointCount(m_ui.pointCountSpinBox->value());
}


void
PeakShaperDlg::gaussianRadioButtonToggled(bool checked)
{
  if(checked)
    m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::GAUSSIAN);
  else
    m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::LORENTZIAN);
}


void
PeakShaperDlg::lorentzianRadioButtonToggled(bool checked)
{
  if(checked)
    m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::LORENTZIAN);
  else
    m_config.setPeakShapeType(msXpSlibmass::PeakShapeType::GAUSSIAN);
}



void
PeakShaperDlg::message(const QString &message, int timeout)
{
  m_ui.messageLineEdit->setText(message);

  QTimer::singleShot(timeout, [this]() { m_ui.messageLineEdit->setText(""); });
}


void
PeakShaperDlg::toFwhm()
{
  qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  // The user has selected (or not) a peak centroid m/z value and wants to
  // compute the corresponding FWHM value based on the resolution.

  int resolution = m_ui.resolutionSpinBox->value();
  if(!resolution)
    {
      message(
        "Please, set a resolution, select the peak centroid m/z value of "
        "interest first.");
      return;
    }

  QTextCursor cursor = m_ui.inputDataPointsPlainTextEdit->textCursor();

  QString selectedText = cursor.selectedText();

  // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
  //<< "Selected text:" << selectedText;

  bool ok   = false;
  double mz = selectedText.toDouble(&ok);

  if(!ok)
    {
      message("The selected text does not convert to a double value.");

      return;
    }

  // Now that we have a mz, we can compute the various values.

  double fwhm = mz / resolution;

  m_ui.fwhmLabel->setText(QString("%1").arg(fwhm, 0, 'f', 5));
}


void
PeakShaperDlg::run()
{
  if(!fetchValidateInputData())
    {
      QMessageBox::warning(
        0,
        tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
        tr(
          "Please, insert at least one (m/z i) pair in the following "
          "format:\n\n"
          "<number><separator><number>\n\n"
          "With <separator> being any non numerical character \n"
          "(space or non-'.' punctuation, for example).\n\n"
          "Also, make sure that you fill-in the resolution or the FWHM value."),
        QMessageBox::Ok);

      return;
    }

  // At this point all the data are set, we can start the computation on all
  // the various peak shapers.

  double minMz = std::numeric_limits<double>::max();
  double maxMz = std::numeric_limits<double>::min();

  int processed = 0;

  double fwhm = 0;

  for(int iter = 0; iter < m_peakShapers.size(); ++iter)
    {
      msXpSlibmass::PeakShaper *peak_shaper_p = m_peakShapers.at(iter);

      double mz = peak_shaper_p->getPeakCentroid().key();

      // Let's take the fwhm value out of the peak shaper in the middle of the
      // list.
      if(iter == m_peakShapers.size() / 2)
        {
          msXpSlibmass::PeakShaperConfig config = peak_shaper_p->getConfig();
          fwhm                                  = config.fwhm(mz);
        }

      if(!peak_shaper_p->computePeakShape())
        {
          QMessageBox::warning(
            0,
            tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"),
            QString("Failed to compute a Trace for peak shape at m/z %1.")
              .arg(peak_shaper_p->getPeakCentroid().key(), QMessageBox::Ok));

          return;
        }

      // Now that we have computed the full shape of the centroid, we'll be
      // able to extract the smallest and greatest mz values of the whole
      // shape.

      if(peak_shaper_p->getTrace().size())
        {
          double smallestMz = peak_shaper_p->getTrace().first()->key();
          double greatestMz = peak_shaper_p->getTrace().last()->key();

          if(greatestMz > maxMz)
            maxMz = greatestMz;
          if(smallestMz < minMz)
            minMz = smallestMz;
        }

      // We want to store a list of all the mz steps for each created spectrum
      // because we'll need it to know what the size of the final mass spectrum
      // bins is going to be.

      // Make a copy of the peak shaper config, because accessing it modifies
      // it.
      msXpSlibmass::PeakShaperConfig config(peak_shaper_p->getConfig());

      ++processed;
    }

  // Allocate a combination MassSpectrum because we'll have to do binning,
  // thus
  // we
  // cannot use a combination Trace.
  msXpSlibmass::MassSpectrum combinationSpectrum;

  double binSize = m_ui.binSizeDoubleSpinBox->value();

  if(!binSize)
    {
      binSize = fwhm / 10;

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "The bin size as fwhm/10 is:"
      //<< QString("%1").arg(binSize, 0, 'f', 6);
    }
  else
    {
      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "The bin size set by the user is:"
      //<< QString("%1").arg(binSize, 0, 'f', 6);
    }

  // Now configure the combination spectrum.
  combinationSpectrum.setBinningType(
    msXpS::BinningType::BINNING_TYPE_ARBITRARY);
  combinationSpectrum.setMinMz(minMz);
  combinationSpectrum.setMaxMz(maxMz);
  combinationSpectrum.setBinSize(binSize);
  combinationSpectrum.setBinSizeType(
    msXpS::MassToleranceType::MASS_TOLERANCE_MZ);
  combinationSpectrum.setRemoveZeroValDataPoints(true);
  combinationSpectrum.populateSpectrumWithArbitraryBins();

  // combinationSpectrum.setBinningType(
  // msXpS::BinningType::BINNING_TYPE_DATA_BASED);
  // combinationSpectrum.setMinMz(minMz);
  // combinationSpectrum.setMaxMz(maxMz);
  // combinationSpectrum.setRemoveZeroValDataPoints(true);
  // combinationSpectrum.setupBinnability(m_peakShapers.first()->getTrace());

  for(int iter = 0; iter < m_peakShapers.size(); ++iter)
    {
      combinationSpectrum.combine(m_peakShapers.at(iter)->getTrace());
    }

  message(QString("Successfully processed %1 peak centroids").arg(processed));

  // Now publish the result:

  m_ui.tabWidget->setCurrentIndex(static_cast<int>(TabWidgetPage::RESULTS));

  // First send the mass spectrum name.
  QString mass_spectrum_name = m_ui.massSpectrumNameInputDataLineEdit->text();

  if(mass_spectrum_name.isEmpty())
    mass_spectrum_name = craftMassSpectrumName();

  m_ui.massSpectrumNameInputDataLineEdit->setText(mass_spectrum_name);
  m_ui.massSpectrumNameResultsLineEdit->setText(mass_spectrum_name);
  m_ui.resultPlainTextEdit->appendPlainText(combinationSpectrum.asText());
}


void
PeakShaperDlg::outputFileName()
{
  m_fileName = QFileDialog::getSaveFileName(
    this, tr("Export to text file"), QDir::homePath(), tr("Any file type(*)"));
}


void
PeakShaperDlg::displayMassSpectrum()
{
  MainWindow *main_window = static_cast<MainWindow *>(parent());

  main_window->createMassSpectrumFromXyText(
    m_ui.resultPlainTextEdit->toPlainText(),
    m_ui.massSpectrumNameResultsLineEdit->text());
}

void
PeakShaperDlg::setCentroidData(const QString &data)
{
  m_ui.inputDataPointsPlainTextEdit->setPlainText(data);
}

} // namespace msXpSmineXpert
