/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2020 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
 */


/////////////////////// StdLib includes
#include <cmath>
#include <algorithm>
#include <limits> // for std::numeric_limits


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


/////////////////////// pappsomspp includes
#include <pappsomspp/massspectrum/massspectrum.h>

/////////////////////// Local includes
#include "MassPeakShaperDlg.hpp"
#include "ProgramWindow.hpp"
#include "Application.hpp"


namespace msxps
{
namespace minexpert
{


MassPeakShaperDlg::MassPeakShaperDlg(ProgramWindow *program_window_p,
                                     const QString &applicationName)
  : QDialog(static_cast<QWidget *>(program_window_p)),
    mp_programWindow(program_window_p),
    m_applicationName{applicationName}
{
  if(!program_window_p)
    qFatal("Programming error. Program aborted.");

  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
MassPeakShaperDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  // qDebug();
  writeSettings();
}


MassPeakShaperDlg::~MassPeakShaperDlg()
{
  // qDebug();

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

  writeSettings();
}


//! Save the settings to later restore the window in its same position.
void
MassPeakShaperDlg::writeSettings()
{
  // qDebug();

  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MassPeakShaperDlg");

  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
MassPeakShaperDlg::readSettings()
{
  // qDebug();

  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MassPeakShaperDlg");

  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
MassPeakShaperDlg::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.setMassPeakShapeType(MassPeakShapeType::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,
          &MassPeakShaperDlg::resolutionChanged);

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

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

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

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

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

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

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

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


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


QString
MassPeakShaperDlg::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 name = m_ui.massSpectrumNameResultsLineEdit->text();

  if(name.isEmpty())
    {
      name =
        QString("%1-%2@%3")
          .arg(m_peakShapers.at(0)->getPeakCentroid().x)
          .arg(m_peakShapers.at(m_peakShapers.size() - 1)->getPeakCentroid().x)
          .arg(QTime::currentTime().toString("hh:mm:ss.zzz"));
    }
  else
    {
      name.append("@");
      name.append(QTime::currentTime().toString("hh:mm:ss.zzz"));
    }

  // qDebug() << "Returning mass spectrum name:" << name;
  return name;
}


std::size_t
MassPeakShaperDlg::fillInThePeakShapers()
{
  // qDebug();

  // 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", Qt::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(gEndOfLineRegExp.match(line).hasMatch())
        continue;

      pappso::DataPoint data_point;

      // qDebug() << "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() << "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.x = data_point.x / m_config.getCharge();

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

      m_peakShapers.append(new MassPeakShaper(data_point, m_config));
    }

  // qDebug() << "m_config:" << m_config.asText(800);

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

  // qDebug();

  return m_peakShapers.size();
}


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


pappso::DataPoint
MassPeakShaperDlg::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", Qt::SkipEmptyParts);

  QString line;

  // This is an invalid data point that will be checked.
  pappso::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(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
MassPeakShaperDlg::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", Qt::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<pappso::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(gEndOfLineRegExp.match(line).hasMatch())
        continue;

      pappso::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 pappso::DataPoint &dp1, const pappso::DataPoint &dp2) {
              return dp1.x < dp2.x;
            });

  // 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.x, 0, 'f', 30).arg(dp.x, 0, 'f', 30);

  return result;
}


bool
MassPeakShaperDlg::fetchValidateInputData()
{
  // qDebug();

  // 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() << "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.

  pappso::DataPoint data_point = probeFirstInputDataPoint();

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

      qDebug() << "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.setMassPeakShapeType(MassPeakShapeType::GAUSSIAN);
      m_ui.logPlainTextEdit->appendPlainText("Peak shape is Gaussian\n");
    }
  else
    {
      m_config.setMassPeakShapeType(MassPeakShapeType::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.x;

      double fwhm = m_config.fwhm(mz);

      // qDebug() << "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.x, 0, 'f', 5)
          .arg(fwhm));
    }
  else
    {
      // We will use the FWHM
      m_config.setFwhm(fwhm);

      // qDebug();
      // 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.toString(m_peakShapers.first()->getPeakCentroid().x));

  return true;
}


void
MassPeakShaperDlg::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
MassPeakShaperDlg::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
MassPeakShaperDlg::pointCountChanged(int value)
{
  // The points have changed, we should compute the increment.
  m_config.setPointCount(value);
}


void
MassPeakShaperDlg::gaussianRadioButtonToggled(bool checked)
{
  if(checked)
    m_config.setMassPeakShapeType(MassPeakShapeType::GAUSSIAN);
  else
    m_config.setMassPeakShapeType(MassPeakShapeType::LORENTZIAN);
}


void
MassPeakShaperDlg::lorentzianRadioButtonToggled(bool checked)
{
  if(checked)
    m_config.setMassPeakShapeType(MassPeakShapeType::LORENTZIAN);
  else
    m_config.setMassPeakShapeType(MassPeakShapeType::GAUSSIAN);
}


void
MassPeakShaperDlg::message(const QString &message)
{
  m_ui.messageLineEdit->setText(message);
}


void
MassPeakShaperDlg::toFwhm()
{
  // qDebug();

  // 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() << "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
MassPeakShaperDlg::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();

  m_mapTrace.clear();

  int processed = 0;

  double fwhm = 0;

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

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

      // Let's take the fwhm value out of the peak shaper in the middle of the
      // list.
      if(iter == m_peakShapers.size() / 2)
        {
          MassPeakShaperConfig 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().x, 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().front().x;
          double greatestMz = peak_shaper_p->getTrace().back().x;

          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.
      MassPeakShaperConfig config(peak_shaper_p->getConfig());

      ++processed;
    }

  // We will need to perform combination, positive combinations.

  pappso::MassSpectrumPlusCombiner mass_spectrum_plus_combiner;

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

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

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

  m_mzIntegrationParams =
    pappso::MzIntegrationParams(minMz,
                        maxMz,
                        pappso::BinningType::ARBITRARY,
                        -1,
                        pappso::PrecisionFactory::getDaltonInstance(binSize),
                        false,
                        0,
                        true);

  // Now compute the bins.

  std::vector<double> bins = m_mzIntegrationParams.createBins();

  mass_spectrum_plus_combiner.setBins(bins);

  // Set apart the MapTrace to receive the combination result.

  for(int iter = 0; iter < m_peakShapers.size(); ++iter)
    {
      mass_spectrum_plus_combiner.combine(m_mapTrace,
                                          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));

  m_ui.resultPlainTextEdit->appendPlainText(m_mapTrace.toString());
}


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


void
MassPeakShaperDlg::displayMassSpectrum()
{
  // We must display the result in the mass spectrum-displaying window and in
  // all the pinned_down widgets.

  ProcessingFlow processing_flow;
  processing_flow.setDefaultMzIntegrationParams(m_mzIntegrationParams);

  // We want that the process going on from now on has an unambiguous
  // file/sample name, so we craft the name here.

  QString mass_spectrum_name = craftMassSpectrumName();

  // qDebug() << "Using mass spectrum name:" << mass_spectrum_name;

  mp_programWindow->displayMassSpectralTrace(
    m_mapTrace.toTrace(),
    // m_ui.massSpectrumNameResultsLineEdit->text());
    processing_flow,
    mass_spectrum_name);
}

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


} // namespace minexpert

} // namespace msxps
