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


/////////////////////// Qt includes
#include <QSettings>
#include <QMenuBar>
#include <QMenu>
#include <QDebug>


/////////////////////// pappsomspp includes


/////////////////////// Local includes
#include "MsRunDataSetTableViewWnd.hpp"
#include "MsRunDataSetTableViewItem.hpp"
#include "../nongui/MsRunDataSet.hpp"
#include "ProgramWindow.hpp"
#include "MzIntegrationParamsDlg.hpp"
#include "../nongui/QualifiedMassSpectrumVectorMassDataIntegratorToMz.hpp"
#include "Application.hpp"


namespace msxps
{
namespace minexpert
{


//! Construct an MsRunDataSetTableViewWnd instance.
MsRunDataSetTableViewWnd::MsRunDataSetTableViewWnd(
  QWidget *parent_p,
  ProgramWindow *program_window_p,
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  const QColor &color)
  : QMainWindow(parent_p),
    mp_programWindow(program_window_p),
    mcsp_msRunDataSet(ms_run_data_set_csp),
    m_color(color)
{
  if(program_window_p == Q_NULLPTR)
    qFatal("Programming error.");

  m_ui.setupUi(this);

  if(!initialize())
    qFatal("Programming error.");

  // Note that we need to set the processing_flow mz integration parameters to
  // the ones found in the file during file load:

  // Set the MsRunDataSet pointer that we need when using toString().
  m_processingFlow.setMsRunDataSetCstSPtr(ms_run_data_set_csp);

  m_processingFlow.setDefaultMzIntegrationParams(
    mcsp_msRunDataSet->craftInitialMzIntegrationParams());
}


//! Destruct \c this MsRunDataSetTableViewWnd instance.
MsRunDataSetTableViewWnd::~MsRunDataSetTableViewWnd()
{
  writeSettings();
}


//! Handle the close event.
void
MsRunDataSetTableViewWnd::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  writeSettings();
  hide();
}


//! Write the settings to as to restore the window geometry later.
void
MsRunDataSetTableViewWnd::writeSettings()
{
  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MsRunDataSetTableViewWnd");

  settings.setValue("geometry", saveGeometry());
  settings.setValue("visible", isVisible());
  settings.setValue("topSplitter", m_ui.topSplitter->saveState());
  settings.setValue("bottomSplitter", m_ui.bottomSplitter->saveState());


  settings.endGroup();
}


//! Read the settings to as to restore the window geometry.
void
MsRunDataSetTableViewWnd::readSettings()
{
  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MsRunDataSetTableViewWnd");

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

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


  bool wasVisible = settings.value("visible").toBool();
  setVisible(wasVisible);

  settings.endGroup();
}


//! Initialize the window.
bool
MsRunDataSetTableViewWnd::initialize()
{
  setWindowTitle("mineXpert2 - MS run data set table view");

  // The tool bar will host the main menu button.
  QToolBar *mp_toolBar = QMainWindow::addToolBar("Settings");
  mp_toolBar->setObjectName(QStringLiteral("mp_toolBar"));
  mp_toolBar->setFloatable(false);

  // Construct the context menu that will be associated to the main menu push
  // button.

  createMainMenu();

  // Finally add the main menu push button to the toolbar!
  mp_toolBar->addWidget(mp_mainMenuPushButton);

  // Computing TIC intensity values from the ms run data set table view, we
  // should receive a signal and from the carried double value we need to show a
  // message.

  connect(mp_programWindow,
          &ProgramWindow::ticIntensityValueSignal,
          this,
          &MsRunDataSetTableViewWnd::ticIntensityValue);

  // We want to show the name of the sample as a colored label.

  QPalette palette;
  palette.setColor(QPalette::WindowText, m_color);
  m_ui.sampleNameLabel->setPalette(palette);
  m_ui.sampleNameLabel->setText(
    mcsp_msRunDataSet->getMsRunId()->getSampleName());

  mp_msRunDataSetTableView = new MsRunDataSetTableView(this);

  m_ui.tableViewVerticalLayout->addWidget(mp_msRunDataSetTableView);

  m_ui.filteringOptionsGroupBox->setChecked(false);
  m_ui.filteringOptionsFrame->setVisible(false);

  mcsp_msRunDataSetTree = mcsp_msRunDataSet->getMsRunDataSetTreeCstSPtr();

  mp_msRunDataSetTableViewModel =
    new MsRunDataSetTableViewModel(mcsp_msRunDataSetTree, this);

  mp_msRunDataSetTableViewProxyModel =
    new MsRunDataSetTableViewProxyModel(this);

  mp_msRunDataSetTableViewProxyModel->setFilterKeyColumn(-1);

  mp_msRunDataSetTableView->setModel(mp_msRunDataSetTableViewProxyModel);

  mp_msRunDataSetTableView->setSortingEnabled(true);

  mp_msRunDataSetTableViewProxyModel->setSourceModel(
    mp_msRunDataSetTableViewModel);

  // Now that we have the pointer to the proxymodel, the precision widget that
  // is required to specify the precursor m/z values search tolerance.

  mp_precisionWidget = new pappso::PrecisionWidget(this);
  m_ui.precisionWidgetHorizontalLayout->addWidget(mp_precisionWidget);
  mp_precisionWidget->setToolTip(
    "Set the tolerance with which the m/z values are matched");

  // Immediately set that precision/tolerance to the proxy model, because if the
  // user executes the filtering without setting any tolerance value, the proxy
  // model will have a nullptr precision pointer member and the program will
  // crash.

  // The default value will apply

  mp_msRunDataSetTableViewProxyModel->setPrecision(mp_precision);

  // Make the connections

  connect(mp_precisionWidget,
          &pappso::PrecisionWidget::precisionChanged,
          [this](pappso::PrecisionPtr precision_p) {
            mp_precision = precision_p;
            mp_msRunDataSetTableViewProxyModel->setPrecision(mp_precision);
          });

  connect(m_ui.executeFilteringPushButton, &QPushButton::clicked, [this]() {
    executeFilteringPushButtonClicked();
  });

  connect(m_ui.integrateToMzPushButton, &QPushButton::clicked, [this]() {
    integrateToMz();
  });

  connect(m_ui.integrateToDtPushButton, &QPushButton::clicked, [this]() {
    integrateToDt();
  });

  connect(m_ui.integrateToRtPushButton, &QPushButton::clicked, [this]() {
    integrateToRt();
  });

  connect(m_ui.integrateToIntPushButton, &QPushButton::clicked, [this]() {
    integrateToTicIntensity();
  });

  connect(m_ui.integrateToDtMzPushButton, &QPushButton::clicked, [this]() {
    integrateToDtMz();
  });

  connect(m_ui.integrateToRtDtPushButton, &QPushButton::clicked, [this]() {
    integrateToRtDt();
  });

  connect(m_ui.integrateToRtMzPushButton, &QPushButton::clicked, [this]() {
    integrateToRtMz();
  });

  // When the filtering group box is unchecked, reset the filters and show
  // everything

  connect(
    m_ui.filteringOptionsGroupBox, &QGroupBox::clicked, [this](bool checked) {
      if(!checked)
        {
          // Deactivate the filtering.
          mp_msRunDataSetTableViewProxyModel->convertFilteringStrings(
            0, "", "", "", "", "", "");

          // Force filtering.
          mp_msRunDataSetTableViewProxyModel->invalidate();

          // Now that we have force the "defiltering", we can display the number
          // of spectra in the status bar.

          std::size_t row_count =
            mp_msRunDataSetTableViewProxyModel->rowCount();

          // Do not set a time out for this status bar message.
          statusBar()->showMessage(
            QString("%1 %2 in the mass spectral data set.")
              .arg(row_count)
              .arg(row_count > 1 ? "spectra" : "spectrum"));
        }

      m_ui.filteringOptionsFrame->setVisible(checked);
    });

  readSettings();

  return true;
}


void
MsRunDataSetTableViewWnd::createMainMenu()
{

  mp_mainMenu = new QMenu(this);

  const QIcon main_menu_icon =
    QIcon(":/images/mobile-phone-like-menu-button.svg");
  mp_mainMenuPushButton = new QPushButton(main_menu_icon, QString(""), this);

  // When the main menu push button is clicked, the menu show up.
  connect(mp_mainMenuPushButton, &QPushButton::clicked, [this]() {
    mp_mainMenu->show();
  });

  QAction *show_mz_integration_params_dlg_action_p = new QAction(
    "Open m/z integration params dialog", dynamic_cast<QObject *>(this));
  show_mz_integration_params_dlg_action_p->setShortcut(
    QKeySequence("Ctrl+O, I"));
  show_mz_integration_params_dlg_action_p->setStatusTip(
    "Open m/z integration params dialog");
  connect(show_mz_integration_params_dlg_action_p,
          &QAction::triggered,
          this,
          &MsRunDataSetTableViewWnd::showMzIntegrationParamsDlg);

  mp_mainMenu->addAction(show_mz_integration_params_dlg_action_p);

  // The main menu is under the control of a push button.
  mp_mainMenuPushButton->setMenu(mp_mainMenu);
}


bool
MsRunDataSetTableViewWnd::recordMsFragmentationSpec()
{

  MsFragmentationSpec ms_fragmentation_spec =
    m_processingFlow.getDefaultMsFragmentationSpec();

  ms_fragmentation_spec.setMsLevel(m_ui.msLevelSpinBox->value());

  m_processingFlow.setDefaultMsFragmentationSpec(ms_fragmentation_spec);

  return true;
}


void
MsRunDataSetTableViewWnd::show()
{
  readSettings();

  QMainWindow::show();
}


void
MsRunDataSetTableViewWnd::keyPressEvent(QKeyEvent *event)
{
  // If Ctrl return, run the filter

  if(event->key() == Qt::Key_Return &&
     event->modifiers() & Qt::ControlModifier &&
     m_ui.filteringOptionsGroupBox->isChecked())
    {
      executeFilteringPushButtonClicked();
    }
}


void
MsRunDataSetTableViewWnd::ticIntensityValue(double intensity_value)
{
  statusBar()->showMessage(QString::number(intensity_value, 'g', 6), 2000);
}


void
MsRunDataSetTableViewWnd::showMzIntegrationParamsDlg()
{
  // qDebug();

  if(mp_mzIntegrationParamsDlg == nullptr)
    {
      mp_mzIntegrationParamsDlg = new MzIntegrationParamsDlg(
        this, m_processingFlow.getDefaultMzIntegrationParams(), m_color);

      connect(
        mp_mzIntegrationParamsDlg,
        &MzIntegrationParamsDlg::mzIntegrationParamsDlgShouldBeDestroyedSignal,
        [this]() {
          // qDebug();
          delete QObject::sender();
          mp_mzIntegrationParamsDlg = nullptr;
        });

      connect(mp_mzIntegrationParamsDlg,
              &MzIntegrationParamsDlg::mzIntegrationParamsChangedSignal,
              this,
              &MsRunDataSetTableViewWnd::mzIntegrationParamsChanged);

      mp_mzIntegrationParamsDlg->show();
    }
}


void
MsRunDataSetTableViewWnd::mzIntegrationParamsChanged(
  pappso::MzIntegrationParams mz_integration_params)
{
  // qDebug().noquote() << "The validated mz integration parameters:"
  //<< mz_integration_params.toString();

  // We cannot copy the params returned as parameter into the destination
  // because we would loose the greatestMz/smallesMz values that we got from the
  // ms run data set upon creation of this window.

  // Create a local copy of the processing flow's default mz integration params.
  pappso::MzIntegrationParams local_mz_integration_params =
    m_processingFlow.getDefaultMzIntegrationParams();

  // Update some of the values with the params after change.
  local_mz_integration_params.setBinningType(
    mz_integration_params.getBinningType());
  local_mz_integration_params.setPrecision(
    mz_integration_params.getPrecision());
  local_mz_integration_params.setDecimalPlaces(
    mz_integration_params.getDecimalPlaces());
  local_mz_integration_params.setRemoveZeroValDataPoints(
    mz_integration_params.isRemoveZeroValDataPoints());

  // Now set the updated mz integration params back to the m_processingFlow.

  m_processingFlow.setDefaultMzIntegrationParams(local_mz_integration_params);

  // qDebug() << "Set new mz integ params to m_processingFlow:"
  //<< local_mz_integration_params.toString();
}


void
MsRunDataSetTableViewWnd::executeFilteringPushButtonClicked()
{
  // qDebug();

  // We need to get all the strings from the various line edit widgets.
  std::size_t ms_level           = m_ui.msLevelSpinBox->value();
  QString indices                = m_ui.indexLineEdit->text();
  QString rt_values              = m_ui.retentionTimeLineEdit->text();
  QString dt_values              = m_ui.driftTimeLineEdit->text();
  QString precursor_index_values = m_ui.precursorIndexLineEdit->text();

  QString precursor_mz_values = m_ui.precursorMzLineEdit->text();
  // qDebug() << "The precursor mz values in the table cell:"
  //<< precursor_mz_values;

  QString precursor_charge_values = m_ui.precursorChargeLineEdit->text();
  // qDebug() << "The precursor charge values in the table cell:"
  //<< precursor_charge_values;

  // Now set all these values to the model proxy which will use them.
  // Note that the precision/tolerance for the mz value matches is automatically
  // set to the proxy model upon modifying values in the widget (see connection
  // at initialization).

  bool res = mp_msRunDataSetTableViewProxyModel->convertFilteringStrings(
    ms_level,
    indices,
    rt_values,
    dt_values,
    precursor_index_values,
    precursor_mz_values,
    precursor_charge_values);

  // qDebug() << "res:" << res;

  if(!res)
    {
      statusBar()->showMessage(
        "A problem with at least one filtering data entity was encountered",
        2000);

      return;
    }

  // Since the filter string conversion worked, go on forcing filtering.

  mp_msRunDataSetTableViewProxyModel->invalidate();

  std::size_t row_count = mp_msRunDataSetTableViewProxyModel->rowCount();

  // Do not set a time out for this status bar message.
  statusBar()->showMessage(QString("%1 %2 retained after filtering.")
                             .arg(row_count)
                             .arg(row_count > 1 ? "spectra" : "spectrum"));
}


void
MsRunDataSetTableViewWnd::integrateToRt()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_RT";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_RT"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  // qDebug() << "begin_rt:" << begin_rt;
  // qDebug() << "end_rt:" << end_rt;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToRt(
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


void
MsRunDataSetTableViewWnd::integrateToMz()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_MZ";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_MZ"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  // qDebug() << "begin_rt:" << begin_rt;
  // qDebug() << "end_rt:" << end_rt;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateToMz(
    nullptr, vector_of_qualified_mass_spectra_sp, local_processing_flow);
}


void
MsRunDataSetTableViewWnd::integrateToDt()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_DT";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_DT"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  // Finally make a shared pointer with the vector of qualified mass spectra.
  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToDt(
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


void
MsRunDataSetTableViewWnd::integrateToTicIntensity()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_INT";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_INT"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  // qDebug() << "begin_rt:" << begin_rt;
  // qDebug() << "end_rt:" << end_rt;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  // Finally make a shared pointer with the vector of qualified mass spectra.
  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToTicIntensity(
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


void
MsRunDataSetTableViewWnd::integrateToDtMz()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_DT_MZ";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_DT_MZ"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  // Finally make a shared pointer with the vector of qualified mass spectra.
  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToDtRtMz(
    pappso::DataKind::dt,
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


void
MsRunDataSetTableViewWnd::integrateToRtMz()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  // qDebug() << "New spec: DATA_TABLE_VIEW_TO_RT_MZ";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_RT_MZ"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  // Finally make a shared pointer with the vector of qualified mass spectra.
  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToDtRtMz(
    pappso::DataKind::rt,
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


void
MsRunDataSetTableViewWnd::integrateToRtDt()
{
  // We need to craft a brand new processing flow because we do not want to
  // pollute the member datum.

  recordMsFragmentationSpec();

  ProcessingFlow local_processing_flow(m_processingFlow);

  // Immediately set the mz integration params to the step. These are located in
  // the processing flow as the default mz integartion params.

  // Create the processing step to document the processing.
  ProcessingStep *step_p = new ProcessingStep;

  step_p->setMzIntegrationParams(
    local_processing_flow.getDefaultMzIntegrationParams());

  // The ProcessingSpec below does not have any range data, but this is no
  // problem.
  ProcessingSpec *spec_p = new ProcessingSpec();

  // Aggregate the spec to the step.
  qDebug() << "New spec: DATA_TABLE_VIEW_TO_RT_DT";
  step_p->newSpec(ProcessingType("DATA_TABLE_VIEW_TO_RT_DT"), spec_p);

  // At this point, make sure we document in the processing flow the other
  // critical parameters: the RT and DT values along with the MS fragmentation
  // spec data. These elements are not taken into the account in the integration
  // itself, because the integration works by going through the list of
  // currently selected items in the table view, but they will be important in
  // subsequent integrations are performed starting from the trace generated by
  // the present integration. Otherwise we loose the fundamental concept of
  // "virtually restricting" the data set to the innermost ranges used all along
  // the integrations.

  using VectorOfQualMassSpecCstSPtrSPtr =
    std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>;

  double begin_rt = -1;
  double end_rt   = -1;

  double begin_dt = -1;
  double end_dt   = -1;

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra =
      recordQualifiedMassSpectraToIntegrate(begin_rt, end_rt, begin_dt, end_dt);

  if(!vector_of_qualified_mass_spectra.size())
    return;

  spec_p = new ProcessingSpec(begin_rt, end_rt);

  // Aggregate the new spec to the step.
  // qDebug() << "New spec: RT_TO_ANY";
  step_p->newSpec("RT_TO_ANY", spec_p);

  // For the DT of course this is only necessary if the data are from an ion
  // mobility mass spectrometry experiment.
  if(begin_dt != -1 && end_dt != -1)
    {
      spec_p = new ProcessingSpec(begin_dt, end_dt);

      // Aggregate the new spec to the step.
      // qDebug() << "New spec: DT_TO_ANY";
      step_p->newSpec("DT_TO_ANY", spec_p);
    }

  // We have finished documenting the flow's step, append it to the processing
  // flow.
  local_processing_flow.push_back(step_p);

  // qDebug().noquote() << "Right before integrating, processing flow:"
  //<< local_processing_flow.toString();

  // Finally make a shared pointer with the vector of qualified mass spectra.
  VectorOfQualMassSpecCstSPtrSPtr vector_of_qualified_mass_spectra_sp =
    std::make_shared<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>(
      vector_of_qualified_mass_spectra);

  mp_programWindow->integrateFromMsRunDataSetTableViewToRtDt(
    pappso::DataKind::rt,
    mcsp_msRunDataSet,
    vector_of_qualified_mass_spectra_sp,
    local_processing_flow,
    m_color);
}


std::vector<pappso::QualifiedMassSpectrumCstSPtr>
MsRunDataSetTableViewWnd::recordQualifiedMassSpectraToIntegrate(
  double &begin_rt, double &end_rt, double &begin_dt, double &end_dt) const
{
  // qDebug();

  std::vector<pappso::QualifiedMassSpectrumCstSPtr>
    vector_of_qualified_mass_spectra;

  // QList<QModelIndex> selected_indices_list =
  // mp_msRunDataSetTableViewSelectionModel->selectedIndexes();

  // qDebug() << "The number of selected indices is:"
  //<< selected_indices_list.size();

  QItemSelectionModel *selection_model_p =
    mp_msRunDataSetTableView->selectionModel();

  QModelIndexList selected_rows_list = selection_model_p->selectedRows();

  // qDebug() << "The number of selected rows is:" << selected_rows_list.size();

  double smallest_rt_value = std::numeric_limits<double>::max();
  double greatest_rt_value = std::numeric_limits<double>::min();

  double smallest_dt_value = std::numeric_limits<double>::max();
  double greatest_dt_value = std::numeric_limits<double>::min();

  for(int iter = 0; iter < selected_rows_list.size(); ++iter)
    {
      QModelIndex index = selected_rows_list.at(iter);

      // Attention, when working with proxy filter models, the internal
      // pointer to the table view item is no more the one that points to the
      // initially created view item. We need to step up to the source model
      // to get the index to the "original" table view item. So, here we
      // convert that proxy index to a source model index

      QModelIndex source_index =
        mp_msRunDataSetTableViewProxyModel->mapToSource(index);

      // Try to get the data.

      QModelIndex sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_SPECTRUM_INDEX));
      QString spectrum_index_string = sibling_index.data().toString();

      sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_RT));
      QString rt_string = sibling_index.data().toString();

      bool ok = false;

      double rt_value = rt_string.toDouble(&ok);

      if(!ok)
        qFatal("Programming error.");

      if(rt_value < smallest_rt_value)
        smallest_rt_value = rt_value;

      if(rt_value > greatest_rt_value)
        greatest_rt_value = rt_value;

      sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_DT));
      QString dt_string = sibling_index.data().toString();

      // This one is conditional: only if there are mobility data.

      if(!dt_string.isEmpty())
        {
          double dt_value = dt_string.toDouble(&ok);

          if(!ok)
            qFatal("Programming error.");

          if(dt_value > -1)
            {
              if(dt_value < smallest_dt_value)
                smallest_dt_value = dt_value;

              if(dt_value > greatest_dt_value)
                greatest_dt_value = dt_value;
            }
        }

      // qDebug() << "From the table view -- spectrum_index_string:"
      //<< spectrum_index_string << "rt_string:" << rt_string
      //<< "dt_string:" << dt_string;

      // Try to get the item itself that contains interesting data:

      MsRunDataSetTableViewItem *view_item_p =
        static_cast<MsRunDataSetTableViewItem *>(
          source_index.internalPointer());

      if(view_item_p == nullptr)
        qFatal("Pointer cannot be nullptr.");

      pappso::QualifiedMassSpectrumCstSPtr qualified_mass_spectrum_csp =
        view_item_p->getQualifiedMassSpectrum();

      vector_of_qualified_mass_spectra.push_back(qualified_mass_spectrum_csp);

      // qDebug()
      //<< "From the qualified mass spectrum:"
      //<< "mass spectrum index:"
      //<< qualified_mass_spectrum_csp->getMassSpectrumId().getSpectrumIndex()
      //<< "rt:" << qualified_mass_spectrum_csp->getRtInMinutes()
      //<< "dt:" << qualified_mass_spectrum_csp->getDtInMilliSeconds();
    }

  // At this point, we know the rt ranges and potentially also the dt ranges.

  begin_rt = smallest_rt_value;
  // qDebug() << "begin_rt:" << begin_rt;

  end_rt = greatest_rt_value;
  // qDebug() << "end_rt:" << end_rt;

  if(smallest_dt_value != std::numeric_limits<double>::max() &&
     greatest_dt_value != std::numeric_limits<double>::min())
    {
      begin_dt = smallest_dt_value;
      // qDebug() << "begin_dt:" << begin_rt;

      end_dt = greatest_dt_value;
      // qDebug() << "end_dt:" << end_rt;
    }

  // qDebug() << "Returning a vector of qualified mass spectra of size:"
  //<< vector_of_qualified_mass_spectra.size();

  statusBar()->showMessage(QString("%1 spectra stored for later integration.")
                             .arg(vector_of_qualified_mass_spectra.size()),
                           2000);

  return vector_of_qualified_mass_spectra;
}


void
MsRunDataSetTableViewWnd::integrationRangesForRtAndDt(double &begin_rt,
                                                      double &end_rt,
                                                      double &begin_dt,
                                                      double &end_dt) const
{
  // qDebug();

  // QList<QModelIndex> selected_indices_list =
  // mp_msRunDataSetTableViewSelectionModel->selectedIndexes();

  // qDebug() << "The number of selected indices is:"
  //<< selected_indices_list.size();

  QItemSelectionModel *selection_model_p =
    mp_msRunDataSetTableView->selectionModel();

  QModelIndexList selected_rows_list = selection_model_p->selectedRows();

  // qDebug() << "The number of selected rows is:" << selected_rows_list.size();

  double smallest_rt_value = std::numeric_limits<double>::max();
  double greatest_rt_value = std::numeric_limits<double>::min();

  double smallest_dt_value = std::numeric_limits<double>::max();
  double greatest_dt_value = std::numeric_limits<double>::min();

  for(int iter = 0; iter < selected_rows_list.size(); ++iter)
    {
      QModelIndex index = selected_rows_list.at(iter);

      // Attention, when working with proxy filter models, the internal
      // pointer to the table view item is no more the one that points to the
      // initially created view item. We need to step up to the source model
      // to get the index to the "original" table view item. So, here we
      // convert that proxy index to a source model index

      // QModelIndex source_index =
      // mp_msRunDataSetTableViewProxyModel->sourceModel()->index(
      // index.row(), index.column(), index.parent());

      QModelIndex source_index =
        mp_msRunDataSetTableViewProxyModel->mapToSource(index);

      // Try to get the data.

      QModelIndex sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_SPECTRUM_INDEX));
      QString spectrum_index_string = sibling_index.data().toString();

      sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_RT));
      QString rt_string = sibling_index.data().toString();

      bool ok = false;

      double rt_value = rt_string.toDouble(&ok);

      if(!ok)
        qFatal("Programming error.");

      if(rt_value < smallest_rt_value)
        smallest_rt_value = rt_value;

      if(rt_value > greatest_rt_value)
        greatest_rt_value = rt_value;

      sibling_index = source_index.siblingAtColumn(
        static_cast<int>(MassSpecDataViewColumns::COLUMN_DT));
      QString dt_string = sibling_index.data().toString();

      // This one is conditional: only if there are mobility data.

      if(!dt_string.isEmpty() && dt_string > -1)
        {
          double dt_value = dt_string.toDouble(&ok);

          if(!ok)
            qFatal("Programming error.");

          if(dt_value < smallest_dt_value)
            smallest_dt_value = dt_value;

          if(dt_value > greatest_dt_value)
            greatest_dt_value = dt_value;
        }

      // qDebug() << "From the table view -- spectrum_index_string:"
      //<< spectrum_index_string << "rt_string:" << rt_string
      //<< "dt_string:" << dt_string;
    }

  // At this point, we know the rt ranges and potentially also the dt ranges.

  begin_rt = smallest_rt_value;
  // qDebug() << "begin_rt:" << begin_rt;

  end_rt = greatest_rt_value;
  // qDebug() << "end_rt:" << end_rt;

  if(smallest_dt_value != std::numeric_limits<double>::max() &&
     greatest_dt_value != std::numeric_limits<double>::min())
    {
      begin_dt = smallest_dt_value;
      // qDebug() << "begin_dt:" << begin_rt;

      end_dt = greatest_dt_value;
      // qDebug() << "end_dt:" << end_rt;
    }
}


} // namespace minexpert

} // namespace msxps
