/* 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
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <thread>
#include <ctime>


/////////////////////// Qt includes
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
#include <QMenuBar>
#include <QMenu>
#include <QKeySequence>
#include <QSettings>
#include <QCloseEvent>
#include <QThread>
#include <QDateTime>
#include <QTimer>
#include <QThread>
#include <QDateTime>

/////////////////////// pappsomspp includes
#include <pappsomspp/processing/combiners/tracepluscombiner.h>


/////////////////////// Local includes
#include "Application.hpp"
#include "ProgramWindow.hpp"
#include "MsRunDataSetTableView.hpp"
#include "MsRunDataSetTableViewModel.hpp"
#include "MsRunDataSetTableViewProxyModel.hpp"
#include "../nongui/globals.hpp"
#include "MsRunReadTask.hpp"
#include <pappsomspp/processing/combiners/mzintegrationparams.h>
#include "../nongui/MassDataIntegrator.hpp"
#include "ColorSelector.hpp"
#include "MsRunSelectorDlg.hpp"
#include "../nongui/MassDataIntegratorTask.hpp"
#include "../nongui/MsRunDataSet.hpp"
#include "TicXicChromMassSpecColorMapWnd.hpp"
#include "../nongui/MsRunDataSetTreeMassDataIntegratorToMz.hpp"


namespace msxps
{
namespace minexpert
{

ProgramWindow::ProgramWindow(QWidget *parent, const QString &module_name)
  : QMainWindow(parent), m_moduleName(module_name)
{
  setWindowTitle(m_moduleName);

  m_lastUsedDirectory = QDir::home().absolutePath();

  // This attribute make sure that the main window of the program is destroyed
  // when it is closed. Effectively stopping the program.
  setAttribute(Qt::WA_DeleteOnClose);

  setWindowIcon(QIcon(":/images/icons/32x32/minexpert2.png"));

  setupWindow();
}


ProgramWindow::~ProgramWindow()
{
  delete mpa_analysisFile;
  delete mpa_analysisPreferences;

  writeSettings();
}


const QString &
ProgramWindow::moduleName() const
{
  return m_moduleName;
}


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

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

  settings.endGroup();
}


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

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

  settings.endGroup();
}


void
ProgramWindow::initializeAllWindows()
{
  mp_openMsRunDataSetsDlg = new OpenMsRunDataSetsDlg(this);
  // mp_openMsRunDataSetsDlg->show();

  mp_ticXicChromPlotWnd = new TicXicChromTracePlotWnd(
    this, "TIC/XIC chromatograms", "TicXicChromTracePlotWnd");
  // mp_ticXicChromPlotWnd->show();

  mp_massSpecPlotWnd =
    new MassSpecTracePlotWnd(this, "Mass spectra", "MassSpecTracePlotWnd");
  // mp_massSpecPlotWnd->show();

  mp_driftSpecPlotWnd =
    new DriftSpecTracePlotWnd(this, "Drift spectra", "DriftSpecTracePlotWnd");
  // mp_driftSpecPlotWnd->show();

  mp_ticXicChromMassSpecColorMapWnd =
    new TicXicChromMassSpecColorMapWnd(this,
                                       "TIC/XIC chrom. / Mass spec. color maps",
                                       "TicXicChromMassSpecColorMapWnd");
  // mp_ticXicChromMassSpecColorMapWnd->show();

  mp_ticXicChromDriftSpecColorMapWnd = new TicXicChromDriftSpecColorMapWnd(
    this,
    "TIC/XIC chrom. / Drift spec. color maps",
    "TicXicChromDriftSpecColorMapWnd");
  // mp_ticXicChromDriftSpecColorMapWnd->show();

  mp_driftSpecMassSpecColorMapWnd =
    new DriftSpecMassSpecColorMapWnd(this,
                                     "Drift spec. / Mass spec. color maps",
                                     "DriftSpecMassSpecColorMapWnd");
  // mp_driftSpecMassSpecColorMapWnd->show();

  mp_xicExtractionWnd = new XicExtractionWnd(
    this, "XIC chromatogram extraction", "XicExtractionWnd");

  mp_taskMonitorWnd =
    new TaskMonitorWnd(this, "Task monitors", "TaskMonitorWnd");
  // mp_taskMonitorWnd->show();

  mp_consoleWnd = new ConsoleWnd(this, m_moduleName);
  // mp_consoleWnd->show();

  // NO, this one is "on demand".
  // mp_isoSpecDlg = new IsoSpecDlg(this, m_moduleName);

  // NO, this one is "on demand".
  // mp_massPeakShaperDlg = new MassPeakShaperDlg(this, m_moduleName);


  // At this point, try to check if we should remind the user to
  // cite the paper.

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

  settings.beginGroup("Globals");

  int run_count = settings.value("run_count", 0).toInt();

  ++run_count;

  if(run_count == 15)
    {
      AboutDlg *dlg = showAboutDlg();

      dlg->showHowToCiteTab();

      settings.setValue("run_count", 0);
    }
  else
    {
      settings.setValue("run_count", run_count);
    }

  settings.endGroup();
}


void
ProgramWindow::createMenusAndActions()
{

  // File menu
  mp_fileMenu = menuBar()->addMenu("&File");

  mp_openFullMsFileAct =
    new QAction("Open mass spectrum file(s) fully in &memory",
                dynamic_cast<QObject *>(this));
  mp_openFullMsFileAct->setStatusTip("Open mass data file(s) fully in memory");
  mp_openFullMsFileAct->setShortcut(QKeySequence("Ctrl+O, M"));

  connect(mp_openFullMsFileAct, &QAction::triggered, [this]() {
    openMassSpectrometryFileDlg(m_lastUsedDirectory, true);
  });

  mp_fileMenu->addAction(mp_openFullMsFileAct);

  mp_openStreamedMsFileAct =
    new QAction("Open mass spectrum file(s) in &streamed mode",
                dynamic_cast<QObject *>(this));
  mp_openStreamedMsFileAct->setStatusTip(
    "Open mass data file(s) in streamed mode");
  mp_openStreamedMsFileAct->setShortcut(QKeySequence("Ctrl+O, S"));

  connect(mp_openStreamedMsFileAct, &QAction::triggered, [this]() {
    openMassSpectrometryFileDlg(m_lastUsedDirectory, false);
  });

  mp_fileMenu->addAction(mp_openStreamedMsFileAct);

  QAction *open_ms_file_from_clipboard_act_p = new QAction(
    "Load mass spectrum from &clipboard", dynamic_cast<QObject *>(this));
  open_ms_file_from_clipboard_act_p->setStatusTip(
    "Load a mass spectrum directly from the clipboard");
  open_ms_file_from_clipboard_act_p->setShortcut(QKeySequence("Ctrl+L, C"));

  connect(open_ms_file_from_clipboard_act_p, &QAction::triggered, [this]() {
    openMassSpectrometryFileFromClipBoard();
  });

  mp_fileMenu->addAction(open_ms_file_from_clipboard_act_p);

  mp_fileMenu->addSeparator();

  QAction *analysis_preferences_act_p =
    new QAction("&Analysis preferences...", dynamic_cast<QObject *>(this));
  analysis_preferences_act_p->setStatusTip(tr("Set the analysis preferences"));
  analysis_preferences_act_p->setShortcut(QKeySequence("Ctrl+A, P"));

  connect(analysis_preferences_act_p, &QAction::triggered, [this]() {
    openAnalysisPreferencesDlg();
  });
  mp_fileMenu->addAction(analysis_preferences_act_p);

  mp_fileMenu->addSeparator();

  mp_quitAct = new QAction(tr("&Quit"), dynamic_cast<QObject *>(this));
  mp_quitAct->setStatusTip(tr("Exit mineXpert"));
  mp_quitAct->setShortcut(tr("Ctrl+Q"));

  connect(mp_quitAct, &QAction::triggered, this, &ProgramWindow::close);
  mp_fileMenu->addAction(mp_quitAct);

  // Windows menu
  mp_windowsMenu = menuBar()->addMenu("&Windows");

  QAction *action_p = new QAction("Show &TIC/XIC chromatograms window",
                                  dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, R"));
  action_p->setStatusTip("Show the TIC/XIC chromatograms window");
  connect(action_p,
          &QAction::triggered,
          this,
          &ProgramWindow::showTicXicChromatogramsWnd);
  mp_windowsMenu->addAction(action_p);


  action_p =
    new QAction("Show &mass spectra window", dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, M"));
  action_p->setStatusTip("Show the mass spectra window");
  connect(
    action_p, &QAction::triggered, this, &ProgramWindow::showMassSpectraWnd);
  mp_windowsMenu->addAction(action_p);


  action_p =
    new QAction("Show &drift spectra window", dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, D"));
  action_p->setStatusTip("Show the drift spectra window");
  connect(
    action_p, &QAction::triggered, this, &ProgramWindow::showDriftSpectraWnd);
  mp_windowsMenu->addAction(action_p);


  action_p = new QAction("Show TIC/&XIC chrom. / mass spec. color maps window",
                         dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, I"));
  action_p->setStatusTip(
    "Show the TIC/XIC chrom. / mass spectra color maps window");
  connect(action_p,
          &QAction::triggered,
          this,
          &ProgramWindow::showTicXicChromMassSpecColorMapWnd);
  mp_windowsMenu->addAction(action_p);


  action_p = new QAction("Show TIC/XIC chrom. / drift &spec. color maps window",
                         dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, S"));
  action_p->setStatusTip(
    "Show the TIC/XIC chrom. / drift spectra color maps window");
  connect(action_p,
          &QAction::triggered,
          this,
          &ProgramWindow::showTicXicChromDriftSpecColorMapWnd);
  mp_windowsMenu->addAction(action_p);


  action_p = new QAction("Show drift s&pec. / mass spec. color maps window",
                         dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, P"));
  action_p->setStatusTip(
    "Show the drift spectra / mass spectra color maps window");
  connect(action_p,
          &QAction::triggered,
          this,
          &ProgramWindow::showDriftSpecMassSpecColorMapWnd);
  mp_windowsMenu->addAction(action_p);


  action_p =
    new QAction("Show tas&k monitor window", dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, T"));
  action_p->setStatusTip("Show the task monitor window");
  connect(
    action_p, &QAction::triggered, this, &ProgramWindow::showTaskMonitorWnd);
  mp_windowsMenu->addAction(action_p);


  action_p = new QAction("Show open ms r&un data sets window",
                         dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, O"));
  action_p->setStatusTip("Show the open MS run data sets window");
  connect(action_p,
          &QAction::triggered,
          this,
          &ProgramWindow::showOpenMsRunDataSetsWnd);

  mp_windowsMenu->addAction(action_p);


  action_p = new QAction("Show &console window", dynamic_cast<QObject *>(this));
  action_p->setShortcut(QKeySequence("Ctrl+W, C"));
  action_p->setStatusTip("Show the console window");
  connect(action_p, &QAction::triggered, this, &ProgramWindow::showConsoleWnd);

  mp_windowsMenu->addAction(action_p);

  mp_windowsMenu->addSeparator();

  action_p = new QAction("Save &workspace", dynamic_cast<QObject *>(this));
  action_p->setStatusTip("Save the current layout of the workspace");
  connect(action_p, &QAction::triggered, this, &ProgramWindow::saveWorkspace);

  mp_windowsMenu->addAction(action_p);


  // Utilities menu
  mp_utilitiesMenu = menuBar()->addMenu("&Utilities");

  action_p =
    new QAction("XIC chromatogram &extractions", dynamic_cast<QObject *>(this));
  action_p->setStatusTip("Open the XIC chromatogram extraction functionality");
  action_p->setShortcut(tr("Ctrl+X, E"));

  connect(
    action_p, &QAction::triggered, this, &ProgramWindow::showXicExtractionWnd);

  mp_utilitiesMenu->addAction(action_p);

  action_p = new QAction("&Isotopic cluster calculations",
                         dynamic_cast<QObject *>(this));
  action_p->setStatusTip("Open the isotopic cluster calculation functionality");
  action_p->setShortcut(QKeySequence("Ctrl+I, C"));

  connect(action_p, &QAction::triggered, this, &ProgramWindow::showIsoSpecDlg);

  mp_utilitiesMenu->addAction(action_p);

  action_p =
    new QAction("Mass peak &shape calculations", dynamic_cast<QObject *>(this));
  action_p->setStatusTip("Open the mass peak shape calculation functionality");
  action_p->setShortcut(QKeySequence("Ctrl+M, S"));

  connect(
    action_p, &QAction::triggered, this, &ProgramWindow::showMassPeakShaperDlg);

  mp_utilitiesMenu->addAction(action_p);


  // Preferences menu
  mp_preferencesMenu = menuBar()->addMenu("&Preferences");

  action_p =
    new QAction("Set max. thread count", dynamic_cast<QObject *>(this));
  action_p->setStatusTip("Set the maximum number of threads to be used for the integrations");
  action_p->setShortcut(tr("Ctrl+P, T"));

  connect(
    action_p, &QAction::triggered, [this]() 
    {
      std::size_t ideal_thread_count = QThread::idealThreadCount();

      m_maxThreadUseCount = QInputDialog::getInt(this, "Set max. thread count",
          "Count", ideal_thread_count, 1, ideal_thread_count, 1);

      //qDebug() << "Set the max. thread use count to : " << m_maxThreadUseCount;
    });

  mp_preferencesMenu->addAction(action_p);


  // Help menu
  mp_helpMenu = menuBar()->addMenu("&Help");

  action_p = new QAction(QIcon(":/images/icons/svg/help-information-icon.svg"),
                         tr("&About"),
                         dynamic_cast<QObject *>(this));
  action_p->setStatusTip(tr("Show the &application's About box"));
  action_p->setShortcut(QKeySequence("Ctrl+H, A"));

  connect(action_p, &QAction::triggered, this, &ProgramWindow::showAboutDlg);
  mp_helpMenu->addAction(action_p);

  action_p =
    new QAction(QIcon(":/images/icons/svg/help-qt-information-icon.svg"),
                tr("About &Qt"),
                dynamic_cast<QObject *>(this));
  action_p->setStatusTip(tr("Show the Qt &library's About box"));
  connect(action_p, &QAction::triggered, this, &Application::aboutQt);
  action_p->setShortcut(QKeySequence("Ctrl+H, Q"));

  mp_helpMenu->addAction(action_p);
}


void
ProgramWindow::setupWindow()
{
  // qDebug();

  QPixmap pixmap(":/images/msxpertsuite-icon-32.png");
  QIcon icon(pixmap);
  setWindowIcon(icon);

  setAttribute(Qt::WA_DeleteOnClose);
  statusBar()->setSizeGripEnabled(true);

  createMenusAndActions();
  initializeAllWindows();
  readSettings();
}

AboutDlg *
ProgramWindow::showAboutDlg()
{
  // The application name will be set automatically by default parameter
  // value.
  AboutDlg *dlg = new AboutDlg(this, m_moduleName);

  dlg->show();

  return dlg;
}


void 
ProgramWindow::setMaxThreadUseCount(std::size_t count)
{
  m_maxThreadUseCount = count;
}


std::size_t
ProgramWindow::getMaxThreadUseCount()
{
  return m_maxThreadUseCount;
}


TaskMonitorWnd *
ProgramWindow::getTaskMonitorWnd() const
{
  return mp_taskMonitorWnd;
}


bool
ProgramWindow::openMassSpectrometryFileDlg(const QString &dir_name,
                                           bool full_in_memory)
{
  QFileDialog file_dialog(
    this,
    QString("%1 - Select mass spectrometry data file(s)").arg(m_moduleName));

  file_dialog.setFileMode(QFileDialog::ExistingFiles);
  file_dialog.setViewMode(QFileDialog::Detail);
  file_dialog.setAcceptMode(QFileDialog::AcceptOpen);

  // Open the dialog in the right directory.
  if(dir_name.isEmpty())
    file_dialog.setDirectory(m_lastUsedDirectory);
  else
    file_dialog.setDirectory(dir_name);

  QStringList file_names;

  if(file_dialog.exec())
    {
      file_names = file_dialog.selectedFiles();

      // Store the directory name whence the selected files come.

      if(file_names.size())
        {
          QString last_used_directory;
          QString first_file_name = file_names.at(0);
          QFileInfo file_info     = QFileInfo(first_file_name);
          m_lastUsedDirectory     = file_info.absoluteDir().dirName();
        }
    }

  for(int iter = 0; iter < file_names.size(); ++iter)
    {
      QString cur_file_name = file_names.at(iter);

      bool res = openMassSpectrometryFile(cur_file_name, full_in_memory);

      if(!res)
        QMessageBox::warning(this,
                             "Mass spectrometry file loading failed",
                             "The file " + cur_file_name + "failed to load.");
    }

  return true;
}


bool
ProgramWindow::openMassSpectrometryFileFromText(const QString &text,
                                                const QString &sample_name)
{

  // qDebug() << "Opening mass spectrum from text with sample name:"
  //<< sample_name;

  if(text.isEmpty())
    {
      statusBar()->showMessage(QString("The clipboard is empty"), 4000);

      return false;
    }

  // Now write the text to a temporary file and load it.

  // Get a directory suitable for temp files.

  QDir sys_tmp_dir = QDir::temp();

  // Craft the sample name with the date and time

  QString temp_file_name = "mineXpert2-";

  temp_file_name.append(
    QDateTime::currentDateTime().toString("ddMMyyyy-HHmmss"));

  QFile file(sys_tmp_dir.absolutePath() + '/' + temp_file_name);

  // qDebug() << "Opening file: " << file.fileName();

  if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    qFatal("Failed to open destination file.");

  QTextStream out(&file);

  out << text;

  out.flush();

  file.close();

  // qDebug() << "File " << file.fileName() << "has been closed now.";

  bool result = openMassSpectrometryFile(file.fileName(), true, sample_name);

  return result;
}


bool
ProgramWindow::openMassSpectrometryFileFromClipBoard()
{
  QClipboard *clipboard = QGuiApplication::clipboard();
  QString ms_data       = clipboard->text();

  QDateTime current_date_time = QDateTime::currentDateTime();

  QString time_as_string = current_date_time.toString("yyyyMMdd-HHmmss");

  return openMassSpectrometryFileFromText(ms_data,
                                          "clipBoard-" + time_as_string);
}

void
ProgramWindow::displayMassSpectralTrace(pappso::Trace trace,
                                        const ProcessingFlow &processing_flow,
                                        const QString &sample_name)
{
  // qDebug() << "The trace has size:" << trace.size()
  //<< "and sample name:" << sample_name;

  // There are two possibilities:
  //
  // 1. There is not a single MS run data set: we need to create one as a shim
  // 2. There is/are at least one MS run data set: ask the user if they want to
  // anchor the new to one of these by providing a selection list.

  // Check with the OpenMsRunDataSetsDlg if there is at least one MsRunDataSet
  // available.

  // Set the pointer to nullptr, that will help us later.
  MsRunDataSetCstSPtr ms_run_data_set_csp = nullptr;

  QColor color;

  bool ok = false;

  QString local_sample_name;

  std::size_t opened_ms_run_data_sets =
    mp_openMsRunDataSetsDlg->msRunDataSetCount();

  if(opened_ms_run_data_sets)
    {

      // There is a least one MS run data set. Provide a list to the user and
      // let them choose one of these. There might also be a choice to create a
      // shim file.

      // Get a list of the MS run data sets.
      std::vector<MsRunDataSetCstSPtr> ms_run_data_sets =
        mp_openMsRunDataSetsDlg->allMsRunDataSets();

      // qDebug() << "MS run data sets count:" << ms_run_data_sets.size();

      // The list of items' sample names will start with an item
      // allowing the user to ask for the creation of a new MS run data set for
      // the trace to be displayed.
      QStringList item_texts = {"Create a new MS run data set"};

      // Extract a list of strings from that list of widgets.
      for(auto &&item : ms_run_data_sets)
        {
          local_sample_name = item->getMsRunId()->getSampleName();
          // qDebug() << "local_sample_name:" << local_sample_name;

          item_texts.append(local_sample_name);
        }

      // qDebug() << "All the sample names:" << item_texts;

      // Craft an input dialog.

      QInputDialog input_dialog(this);
      input_dialog.setComboBoxItems(item_texts);

      // Now as the user to select one item.

      QString item_text = QInputDialog::getItem(this,
                                                "Select a MS run data set",
                                                "MS run data set:",
                                                item_texts,
                                                0,
                                                false,
                                                &ok);
      if(!ok)
        {
          // The user pressed cancel. Just return.
          return;
        }

      if(item_text != "Create a new MS run data set")
        {
          // Now we know that the user wants the new trace to be anchored to a
          // given MsRunDataSet.

          ok = false;
          ok = mp_openMsRunDataSetsDlg->msRunDataSetFromSampleName(
            item_text, ms_run_data_set_csp, color);

          if(!ok || ms_run_data_set_csp == nullptr)
            qFatal("Programming error.");

          // At this point we know we won't need to create a shim ms run data
          // set because ms_run_data_set_csp is no more nullptr and we also know
          // the color.
        }
      else
        {
          // qDebug() << "Creating a new MS run data set.";
        }
    }
  // End of
  // if(opened_ms_run_data_sets)

  // Now test if we need to create a shim ms run data set or not by checking
  // ms_run_data_set_csp

  if(ms_run_data_set_csp == nullptr)
    {

      // Craft a local sample name

      if(sample_name.isEmpty())
        {
          QDateTime current_date_time = QDateTime::currentDateTime();

          QString time_as_string = current_date_time.toString("yyyyMMdd-HHmm");

          local_sample_name = "mass-spectrum-" + time_as_string;
        }
      else
        local_sample_name = sample_name;

      openMassSpectrometryFileFromText(trace.toString(), local_sample_name);

      // At this point we should have a new file. When the new ms run data set
      // will be added to the open ms run data sets dlg list widget, it will be
      // selected.

      showOpenMsRunDataSetsWnd();

      QMessageBox::information(
        this,
        "Select a MS run data set",
        "One shim mass data file was loaded.\n"
        "Please, perform the task agin and select the right MS run data set.");

      return;
    }

  // At this point we have what we needed, a proper ms run data set pointer!

  ProcessingFlow local_processing_flow(processing_flow);

  local_processing_flow.setMsRunDataSetCstSPtr(ms_run_data_set_csp);

  mp_massSpecPlotWnd->addTracePlot(
    trace, ms_run_data_set_csp, local_processing_flow, color, nullptr);
}


bool
ProgramWindow::openMassSpectrometryFile(const QString &file_name,
                                        bool full_in_memory,
                                        const QString &sample_name)
{
  // qDebug() << "Opening mass spectrometry file:" << file_name
  //<< "with sample name:" << sample_name
  //<< "from thread:" << QThread::currentThread();

  if(file_name.isEmpty() || !QFileInfo::exists(file_name))
    {
      return openMassSpectrometryFileDlg(QDir::homePath(), full_in_memory);
    }

  // At this point, we have a file name. Let's store the directory where the
  // file sits so that later the user can be directed there immediately.

  m_lastUsedDirectory = QFileInfo(file_name).dir().absolutePath();

  // Now let's start the file type identification work.

  // The MsFileAccessor will allow to get a list of MS run ids that can then be
  // used to request a MS run reader appropriate for the MS data file format.
  // Beccause there might be more than one ms run in a single mzML file, we need
  // to have a common prefix for all those ms run ids.
  pappso::MsFileAccessor ms_file_accessor(file_name, m_msRunIdPrefix);

  // qDebug() << "Could get MsFileAccessor.";

  std::vector<pappso::MsRunIdCstSPtr> ms_run_ids =
    ms_file_accessor.getMsRunIds();

  std::size_t run_count = ms_run_ids.size();

  // qDebug() << "The number of ms run ids:" << run_count;

  if(!run_count)
    {
      QMessageBox::information(this,
                               "Open mass spectrometry file",
                               "This file does contain no MS run");
      return false;
    }

  int run_index = 0;

  // qDebug() << "The number of ms runs in the file is:" << run_count;

  // If there are more than one run, let the user select the one they are
  // interested in.

  if(run_count > 1)
    {

      // If the user cancels the dialog, returns -1, otherwise returns the index
      // of the ms run id.

      run_index = selectMsRun(ms_run_ids);

      // qDebug() << "The run_index: " << run_index;

      if(run_index == -1)
        return false;
    }

  // Now actually load the data!

  // Allocate a ms run reader task to read the data.
  MsRunReadTask *ms_run_read_task_p = new MsRunReadTask(this);

  // Get the first run id of the vector.
  pappso::MsRunIdCstSPtr ms_run_id_csp = ms_run_ids.at(run_index);

  if(!sample_name.isEmpty())
    {
      // qDebug() << "The sample name is not empty:" << sample_name;

      std::const_pointer_cast<pappso::MsRunId>(ms_run_id_csp)
        ->setSampleName(sample_name);
    }

  // qDebug() << "The run id as text:" << ms_run_id_csp->toString();

  MassSpecDataFileLoaderSPtr loader_sp =
    std::make_shared<MassSpecDataFileLoader>(ms_file_accessor.getFileName(),
                                             full_in_memory);

  if(loader_sp == nullptr)
    qFatal("Programming error");

  // Allocate a new TaskMonitorCompositeWidget

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Make sure the task monitor window is visible !
  showTaskMonitorWnd();

  // Initialize the monitor composite widget's widgets and make all the
  // connections loader <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_id_csp->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Reading MS run data");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.


  connect(ms_run_read_task_p,
          &MsRunReadTask::finishedReadingMsRunDataSignal,
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          loader_sp.get(),
          &MassSpecDataFileLoader::cancelOperation);

  // We need to register the meta type for std::size_t because otherwise it
  // cannobe shipped though signals.

  qRegisterMetaType<std::size_t>("std::size_t");

  task_monitor_composite_widget_p->makeMassSpecDataFileLoaderConnections(
    loader_sp.get());

  // From the file accessor get a shared pointer to the MS run reader for the ms
  // run id of interest.
  pappso::MsRunReaderSPtr ms_run_reader_sp =
    ms_file_accessor.msRunReaderSp(ms_run_id_csp);

  if(ms_run_reader_sp == nullptr)
    qFatal("Failed to get a MS data file reader.");

  MsRunDataSetSPtr ms_run_data_set_sp =
    std::make_shared<MsRunDataSet>(nullptr /*parent*/, ms_run_reader_sp);

  if(ms_run_data_set_sp == nullptr)
    qFatal("Cannot be that pointer is nullptr");

  if(ms_run_data_set_sp.get() == nullptr)
    qFatal("Cannot be that pointer is nullptr");

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // Inform the loader that the mass spec data is there.
  loader_sp->setMsRunDataSet(ms_run_data_set_sp);

  // qRegisterMetaType<msxps::minexpert::MsRunDataSetSPtr>("msxps::minexpert::MsRunDataSetSPtr");
  qRegisterMetaType<MsRunDataSetSPtr>("MsRunDataSetSPtr");

  // This is how we start the computation in the MsRunReadTask object.
  connect(this,
          &ProgramWindow::readMsRunDataSignal,
          ms_run_read_task_p,
          &MsRunReadTask::readMsRunData,
          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // When the read task finishes, it sends a signal that we trap to go on with
  // the file data loading procedure in another function.

  connect(ms_run_read_task_p,
          &MsRunReadTask::finishedReadingMsRunDataSignal,
          this,
          &ProgramWindow::finishedReadingMsRunData,
          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  ms_run_read_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(ms_run_read_task_p,
          &MsRunReadTask::finishedReadingMsRunDataSignal,
          this,
          [thread_p, ms_run_read_task_p]() {
            // Do not forget that we have to delete the MsRunReadTask allocated
            // instance.
            ms_run_read_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // qDebug() << "Emitting readMsRunDataSignal(ms_run_read_task_p, "
  //"ms_run_reader_sp, loader_sp);";

  emit readMsRunDataSignal(ms_run_read_task_p, ms_run_reader_sp, loader_sp);

  return true;
}


QFile *
ProgramWindow::getAnalysisFilePtr()
{
  return mpa_analysisFile;
}


AnalysisPreferences *
ProgramWindow::getAnalysisPreferences()
{
  return mpa_analysisPreferences;
}


void
ProgramWindow::openAnalysisPreferencesDlg()
{
  if(mp_analysisPreferencesDlg == nullptr)
    {
      mp_analysisPreferencesDlg =
        new AnalysisPreferencesDlg(dynamic_cast<QWidget *>(this), m_moduleName);
    }

  int res = mp_analysisPreferencesDlg->exec();

  if(res == QDialog::Accepted)
    {
      // Copy the preferences from the dialog window to *this.
      mpa_analysisPreferences = new AnalysisPreferences(
        mp_analysisPreferencesDlg->analysisPreferences());

      // Let's check if the recording should go a file, in which case we test
      // that we can open it properly.

      if((mpa_analysisPreferences->m_recordTarget &
          RecordTarget::RECORD_TO_FILE) == RecordTarget::RECORD_TO_FILE)
        {
          // Now check that the analysis file could be opened.

          if(mpa_analysisFile != nullptr)
            {
              delete mpa_analysisFile;
              mpa_analysisFile = nullptr;
            }

          mpa_analysisFile = new QFile(mpa_analysisPreferences->m_fileName);

          bool res = false;

          if(mpa_analysisPreferences->m_fileOpenMode == QIODevice::Truncate)

            // Interestingly, only truncate did not work. Since writeonly
            // implies truncate, that's fine, and it works.

            res = mpa_analysisFile->open(QIODevice::WriteOnly);
          else
            res = mpa_analysisFile->open(QIODevice::Append);

          if(!res)
            {
              delete mpa_analysisFile;
              mpa_analysisFile = nullptr;

              QMessageBox msgBox;
              msgBox.setText(
                "Failed to open the analysis file, please fix "
                "the analysis preferences.");
              msgBox.exec();

              return;
            }
          else
            {
              // Close the file, which will be opened each time it is
              // necessary.
              mpa_analysisFile->close();

              // We can effectively start the work. The windows needing to
              // access the file will call getAnalysisFile() and make sure that
              // it is not nullptr.
            }
        }
    }
  else // if(res == QDialog::Accepted)
    {
      // The dialog returned a rejected result. We cannot do anything.
    }
}


void
ProgramWindow::recordAnalysisStanza(QString stanza, const QColor &color)
{
  // Check that the user configured the analysis preferences.

  if(mpa_analysisPreferences == nullptr)
    return;

  // The record might go to a file or to the console, to the clipboard...
  if((mpa_analysisPreferences->m_recordTarget & RecordTarget::RECORD_TO_FILE) ==
     RecordTarget::RECORD_TO_FILE)
    {
      if(mpa_analysisFile != nullptr)
        {

          // Write the stanza, that was crafted by the calling plot widget to
          // the file.

          if(!mpa_analysisFile->open(QIODevice::Append))
            {
              statusBar()->showMessage(
                QString("Could not record the step because "
                        "the file could not be opened."),
                4000);
            }
          else
            {
              mpa_analysisFile->write(stanza.toLatin1());

              // Force writing because we may want to have tail -f work fine on
              // the file, and see modifications live to change fiels in a text
              // editor.

              mpa_analysisFile->flush();
              mpa_analysisFile->close();
            }
        }
      else
        {
          // qDebug() << "The mpa_analysisFile pointer is nullptr.";

          statusBar()->showMessage(
            QString("Could not record the analysis step to file. "
                    "Please define a file to write the data to."),
            4000);
        }
    }

  // Also, if recording to the console is asked for, then do that also.
  if((mpa_analysisPreferences->m_recordTarget &
      RecordTarget::RECORD_TO_CONSOLE) == RecordTarget::RECORD_TO_CONSOLE)
    {
      logColoredTextToConsole(stanza, color);
    }

  // Also, if recording to the clipboard is asked for, then do that also.
  if((mpa_analysisPreferences->m_recordTarget &
      RecordTarget::RECORD_TO_CLIPBOARD) == RecordTarget::RECORD_TO_CLIPBOARD)
    {
      QClipboard *clipboard = QApplication::clipboard();
      clipboard->setText(clipboard->text() + stanza, QClipboard::Clipboard);
    }

  return;
}


// Here we need to finalize the reading of the ms run data set and also chain
// with the calculation of the corresponding TIC chromatogram.
void
ProgramWindow::finishedReadingMsRunData(MsRunDataSetSPtr ms_run_data_set_sp)
{

  // qDebug() << "Finished reading ms run data for sample name:"
  //<< ms_run_data_set_sp->getMsRunId()->getSampleName();

  // We are here as a slot to a signal sent by the MsRunReadTask.

  // Increment the loaded file count so that next time we'll feed it with a new
  // value to the file accessor constructor (see above).
  ++m_loadedFileCount;

  // Since we are load ms run data from file, we need to provide the user with
  // starting points for their data exploration: a TIC chromatogram at least.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

#if 0
				// Let's control how the data loading process went.

				pappso::MsRunDataSetTreeCstSPtr ms_run_data_set_tree_csp =
					ms_run_data_set_sp->getMsRunDataSetTreeCstSPtr();

				const std::map<std::size_t, pappso::MsRunDataSetTreeNode *> &index_node_map =
					ms_run_data_set_tree_csp->getIndexNodeMap();

				qDebug() << "The number of spectra:"
					<< ms_run_data_set_tree_csp->getSpectrumCount()
					<< "and the data tree has this depth:"
					<< ms_run_data_set_tree_csp->depth()
					<< "and the index node map has this number of items:"
					<< index_node_map.size();
#endif

  // We want to try the multi-visitor approach: a single visitor that contains a
  // vector of visitors. This is interesting because it makes reading the mass
  // spectral data required only once.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  seedInitialTicChromatogramAndMsRunDataSetStatistics(ms_run_data_set_sp);

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();
}


void
ProgramWindow::seedInitialTicChromatogramAndMsRunDataSetStatistics(
  MsRunDataSetSPtr &ms_run_data_set_sp)
{
  if(ms_run_data_set_sp == nullptr)
    qFatal("Cannot be that pointer is nullptr");

  // qDebug() << "sample name:"
  //<< ms_run_data_set_sp->getMsRunId()->getSampleName();

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(
    this,
    &ProgramWindow::seedInitialTicChromatogramAndMsRunDataSetStatisticsSignal,
    mass_data_integrator_task_p,
    &MassDataIntegratorTask::
      seedInitialTicChromatogramAndMsRunDataSetStatistics,
    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(MassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          this,
          &ProgramWindow::
            finishedSeedingInitialTicChromatogramAndMsRunDataSetStatistics,
          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(MassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          this,
          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // No need to provide any processing flow object to the constructor, as we
  // have none yet, this is the very first integration.
  MsRunDataSetTreeMassDataIntegratorToRt *mass_data_integrator_p =
    new MsRunDataSetTreeMassDataIntegratorToRt(ms_run_data_set_sp);

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // Ensure the mass data integrator messages are used.

  connect(mass_data_integrator_p,
          &MassDataIntegrator::logTextToConsoleSignal,
          this,
          &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_sp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to initial TIC chromatogram and calculating MS run "
    "statistics");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(MassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &MassDataIntegrator::cancelOperation);

  // We need to register the meta type for std::size_t because otherwise it
  // cannot be shipped though signals.

  qRegisterMetaType<std::size_t>("std::size_t");

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  // qDebug() << "going to emit integrateToInitialTicChromatogramSignal with
  // mass " "data integrator:"
  //<< mass_data_integrator_p;

  emit seedInitialTicChromatogramAndMsRunDataSetStatisticsSignal(
    mass_data_integrator_p);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(
    this,
    &ProgramWindow::seedInitialTicChromatogramAndMsRunDataSetStatisticsSignal,
    mass_data_integrator_task_p,
    &MassDataIntegratorTask::
      seedInitialTicChromatogramAndMsRunDataSetStatistics);
}


void
ProgramWindow::finishedSeedingInitialTicChromatogramAndMsRunDataSetStatistics(
  MassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // In this calculation, the m_mapTrace object in the integrator is the TIC
  // chroamtogram.

  // The function below uses the mass_data_integrator_p to get to the TIC
  // chromatogram and create the TIC chrom widget.
  std::pair<MsRunDataSetCstSPtr, QColor> result_pair =
    mp_ticXicChromPlotWnd->finishedIntegratingToInitialTicChromatogram(
      static_cast<MsRunDataSetTreeMassDataIntegratorToRt *>(
        mass_data_integrator_p));

  // Make sure the tic chrom wnd is visible!
  showTicXicChromatogramsWnd();

  // qDebug() << "the sample name:"
  //<< result_pair.first->getMsRunId()->getSampleName();

  if(result_pair.first != nullptr)
    {
      // There were data in the data set ! Go on.

      mp_openMsRunDataSetsDlg->newOpenMsRunDataSet(result_pair.first,
                                                   result_pair.second);

      showOpenMsRunDataSetsWnd();
    }

  // As for the statistics of the ms run data set, it has been set
  // to the data set already.
}


void
ProgramWindow::integrateFromMsRunDataSetTableViewToRt(
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_vector_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers that we need to
  // combine.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // Allocate the mass data integrator that is speciaized in the computation of
  // TIC chromaotograms, starting from a vector of qualified mass spectrum
  // pointers.

  QualifiedMassSpectrumVectorMassDataIntegratorToRt *mass_data_integrator_p =
    new QualifiedMassSpectrumVectorMassDataIntegratorToRt(
      ms_run_data_set_csp, processing_flow, qualified_mass_spectra_vector_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToRt *)>(
            &ProgramWindow::integrateQualifiedMassSpectrumVectorToRtSignal),

          mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToRt *)>(
            &MassDataIntegratorTask::integrateToRt),

          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToRt),

          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to XIC chromatogram from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  emit integrateQualifiedMassSpectrumVectorToRtSignal(mass_data_integrator_p);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(this,

             static_cast<void (ProgramWindow::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToRt *)>(
               &ProgramWindow::integrateQualifiedMassSpectrumVectorToRtSignal),

             mass_data_integrator_task_p,

             static_cast<void (MassDataIntegratorTask::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToRt *)>(
               &MassDataIntegratorTask::integrateToRt));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToRt(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // At this point we need to ask the TIC XIC chromatogram window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  pappso::Trace trace = mass_data_integrator_p->getMapTrace().toTrace();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  // The newTrace function ensure that if there are pushpinned widget, the new
  // trace will be handled according to the destination selected (wipe and
  // replace, combine, overlay).
  mp_ticXicChromPlotWnd->newTrace(
    trace, ms_run_data_set_csp, processing_flow, color, nullptr);

  if(!mp_ticXicChromPlotWnd->isVisible())
    mp_ticXicChromPlotWnd->showWindow();

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}


void
ProgramWindow::integrateFromMsRunDataSetTableViewToDt(
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    vector_of_qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers for which we need
  // to calculate a TIC as a function of the drift time.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // Allocate the mass data integrator that is speciaized in the computation of
  // TIC chromaotograms, starting from a vector of qualified mass spectrum
  // pointers.

  QualifiedMassSpectrumVectorMassDataIntegratorToDt *mass_data_integrator_p =
    new QualifiedMassSpectrumVectorMassDataIntegratorToDt(
      ms_run_data_set_csp,
      processing_flow,
      vector_of_qualified_mass_spectra_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.

  connect(this,
          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToDt *)>(
            &ProgramWindow::integrateQualifiedMassSpectrumVectorToDtSignal),
          mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToDt *)>(
            &MassDataIntegratorTask::integrateToDt),
          Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          this,
          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToDt),
          Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          this,
          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to drift spectrum from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  emit integrateQualifiedMassSpectrumVectorToDtSignal(mass_data_integrator_p);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(this,
             static_cast<void (ProgramWindow::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToDt *)>(
               &ProgramWindow::integrateQualifiedMassSpectrumVectorToDtSignal),
             mass_data_integrator_task_p,
             static_cast<void (MassDataIntegratorTask::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToDt *)>(
               &MassDataIntegratorTask::integrateToDt));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToDt(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // At this point we need to ask the drift spectra window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  pappso::Trace trace = mass_data_integrator_p->getMapTrace().toTrace();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  mp_driftSpecPlotWnd->addTracePlot(
    trace, ms_run_data_set_csp, processing_flow, color, nullptr);

  if(!mp_driftSpecPlotWnd->isVisible())
    mp_driftSpecPlotWnd->showWindow();

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}


void
ProgramWindow::integrateFromMsRunDataSetTableViewToMz(
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_vector_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers that we need to
  // combine to produce a combination mass spectrum.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // Allocate the mass data integrator that is speciaized in the computation of
  // mass spectra, starting from a vector of qualified mass spectrum
  // pointers.

  QualifiedMassSpectrumVectorMassDataIntegratorToMz *mass_data_integrator_p =
    new QualifiedMassSpectrumVectorMassDataIntegratorToMz(
      ms_run_data_set_csp, processing_flow, qualified_mass_spectra_vector_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  // qDebug() << "Receiving processing flow:" << processing_flow.toString();

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToMz *)>(
            &ProgramWindow::integrateQualifiedMassSpectrumVectorToMzSignal),

          mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToMz *)>(
            &MassDataIntegratorTask::integrateToMz),

          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToMz),
          //
          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to mass spectrum from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  emit integrateQualifiedMassSpectrumVectorToMzSignal(mass_data_integrator_p);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at integration, it will not be called
  // twice.
  disconnect(this,

             static_cast<void (ProgramWindow::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToMz *)>(
               &ProgramWindow::integrateQualifiedMassSpectrumVectorToMzSignal),

             mass_data_integrator_task_p,

             static_cast<void (MassDataIntegratorTask::*)(
               QualifiedMassSpectrumVectorMassDataIntegratorToMz *)>(
               &MassDataIntegratorTask::integrateToMz));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToMz(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // At this point we need to ask the TIC XIC chromatogram window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  pappso::Trace trace = mass_data_integrator_p->getMapTrace().toTrace();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  // qDebug() << "The processing flow:" << processing_flow.toString();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  // The newTrace function ensure that if there are pushpinned widget, the new
  // trace will be handled according to the destination selected (wipe and
  // replace, combine, overlay).
  mp_massSpecPlotWnd->newTrace(
    trace, ms_run_data_set_csp, processing_flow, color, nullptr);

  if(!mp_massSpecPlotWnd->isVisible())
    mp_massSpecPlotWnd->showWindow();

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}

void
ProgramWindow::integrateFromMsRunDataSetTableViewToTicIntensity(
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_vector_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers that we need to
  // combine.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // Allocate the mass data integrator that is speciaized in the computation of
  // TIC chromaotograms, starting from a vector of qualified mass spectrum
  // pointers.

  QualifiedMassSpectrumVectorMassDataIntegratorToTicInt
    *mass_data_integrator_p =
      new QualifiedMassSpectrumVectorMassDataIntegratorToTicInt(
        ms_run_data_set_csp, processing_flow, qualified_mass_spectra_vector_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(
    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToTicInt *)>(
      &ProgramWindow::integrateQualifiedMassSpectrumVectorToTicIntensitySignal),

    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToTicInt *)>(
      &MassDataIntegratorTask::integrateToTicIntensity),

    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &ProgramWindow::
              finishedIntegratingQualifiedMassSpectrumVectorToTicIntensity),

          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to TIC intensity from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  emit integrateQualifiedMassSpectrumVectorToTicIntensitySignal(
    mass_data_integrator_p);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(
    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToTicInt *)>(
      &ProgramWindow::integrateQualifiedMassSpectrumVectorToTicIntensitySignal),

    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToTicInt *)>(
      &MassDataIntegratorTask::integrateToTicIntensity));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToTicIntensity(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  pappso::Trace trace = mass_data_integrator_p->getMapTrace().toTrace();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  // We now need to display the result into the status bar of the window.

  double tic_intensity =
    static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToTicInt *>(
      mass_data_integrator_p)
      ->getTicIntensity();

  emit(ticIntensityValueSignal(tic_intensity));

  QString sample_name = ms_run_data_set_csp->getMsRunId()->getSampleName();

  QString msg =
    QString("%1: TIC intensity: %2\n").arg(sample_name).arg(tic_intensity);

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  logColoredTextToConsole(msg, color);

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}


void
ProgramWindow::integrateFromMsRunDataSetTableViewToDtRtMz(
  pappso::DataKind data_kind,
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_vector_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers that we need to
  // combine.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // qDebug() << "Number of qualified mass spectra to integrate:"
  //<< qualified_mass_spectra_vector_sp->size();

  QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz
    *mass_data_integrator_p =
      new QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz(
        ms_run_data_set_csp, processing_flow, qualified_mass_spectra_vector_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  qRegisterMetaType<pappso::DataKind>("pappso::DataKind");

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(this,

          static_cast<void (ProgramWindow::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *,
            pappso::DataKind)>(
            &ProgramWindow::integrateQualifiedMassSpectrumVectorToDtRtMzSignal),

          mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *,
            pappso::DataKind)>(&MassDataIntegratorTask::integrateToDtRtMz),

          // Fundamental for signals that travel across QThread instances...
          Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(
    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegrator *)>(
      &MassDataIntegratorTask::finishedIntegratingDataSignal),

    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegrator *)>(
      &ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToDtRtMz),

    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to XIC chromatogram from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  // qDebug() << "Emitting integrateQualifiedMassSpectrumVectorToDtRtMzSignal "
  //"with data_kind"
  //<< static_cast<int>(data_kind);

  emit integrateQualifiedMassSpectrumVectorToDtRtMzSignal(
    mass_data_integrator_p, data_kind);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(
    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *,
      pappso::DataKind)>(
      &ProgramWindow::integrateQualifiedMassSpectrumVectorToDtRtMzSignal),

    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *,
      pappso::DataKind)>(&MassDataIntegratorTask::integrateToDtRtMz));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToDtRtMz(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // At this point we need to ask the proper color map window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  // Get a pointer to the double maptrace map that will serve as the data to
  // fill-in the color map.
  std::shared_ptr<std::map<double, pappso::MapTrace>> double_map_trace_map_sp =
    static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *>(
      mass_data_integrator_p)
      ->getDoubleMapTraceMapSPtr();

  // qDebug() << "rt / mz map size:" << double_map_trace_map_sp->size();

  if(!double_map_trace_map_sp->size())
    {
      qDebug() << "There is not a single item in the rt / mz map.";
      return;
    }

  // What was the kind of data handled in this integration?

  pappso::DataKind data_kind =
    static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *>(
      mass_data_integrator_p)
      ->getLastIntegrationDataKind();

  // We need to have the specifics of the color map: the number of m/z cells,
  // the minimum/maximum key value (the RT values)
  // the minimum/maximum mz value.

  QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz
    *qualified_mass_spectra_integrator =
      static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToDtRtMz *>(
        mass_data_integrator_p);

  pappso::ColorMapPlotConfig color_map_plot_config(
    data_kind,
    pappso::DataKind::mz,
    pappso::AxisScale::orig,
    pappso::AxisScale::orig,
    pappso::AxisScale::orig,
    qualified_mass_spectra_integrator->getColorMapKeyCellCount(),
    qualified_mass_spectra_integrator->getColorMapMzCellCount(),
    qualified_mass_spectra_integrator->getColorMapMinKey(),
    qualified_mass_spectra_integrator->getColorMapMaxKey(),
    qualified_mass_spectra_integrator->getColorMapMinMz(),
    qualified_mass_spectra_integrator->getColorMapMaxMz());

  // qDebug() << "color_map_plot_config:" << color_map_plot_config.toString();

  // Note that we do not support multiple color maps stacked one on top of the
  // other.

  if(data_kind == pappso::DataKind::rt)
    {
      mp_ticXicChromMassSpecColorMapWnd->addColorMapPlot(
        double_map_trace_map_sp,
        color_map_plot_config,
        ms_run_data_set_csp,
        processing_flow,
        color,
        nullptr);
      if(!mp_ticXicChromMassSpecColorMapWnd->isVisible())
        mp_ticXicChromMassSpecColorMapWnd->showWindow();
    }
  else if(data_kind == pappso::DataKind::dt)
    {
      mp_driftSpecMassSpecColorMapWnd->addColorMapPlot(double_map_trace_map_sp,
                                                       color_map_plot_config,
                                                       ms_run_data_set_csp,
                                                       processing_flow,
                                                       color,
                                                       nullptr);
      if(!mp_driftSpecMassSpecColorMapWnd->isVisible())
        mp_driftSpecMassSpecColorMapWnd->showWindow();
    }

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}


void
ProgramWindow::integrateFromMsRunDataSetTableViewToRtDt(
  pappso::DataKind data_kind,
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_vector_sp,
  const ProcessingFlow &processing_flow,
  [[maybe_unused]] const QColor &color)
{
  // We receive a vector of qualified mass spectrum pointers that we need to
  // combine.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_sp.use_count();

  // qDebug() << "Number of qualified mass spectra to integrate:"
  //<< qualified_mass_spectra_vector_sp->size();

  QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *mass_data_integrator_p =
    new QualifiedMassSpectrumVectorMassDataIntegratorToRtDt(
      ms_run_data_set_csp, processing_flow, qualified_mass_spectra_vector_sp);

  mass_data_integrator_p->setMaxThreadUseCount(m_maxThreadUseCount);

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  qRegisterMetaType<pappso::DataKind>("pappso::DataKind");

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(
    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *, pappso::DataKind)>(
      &ProgramWindow::integrateQualifiedMassSpectrumVectorToRtDtSignal),

    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *, pappso::DataKind)>(
      &MassDataIntegratorTask::integrateToRtDt),

    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // When the task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  connect(
    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegrator *)>(
      &MassDataIntegratorTask::finishedIntegratingDataSignal),

    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegrator *)>(
      &ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToRtDt),

    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(mass_data_integrator_task_p,

          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),

          this,

          [thread_p, mass_data_integrator_task_p]() {
            // Do not forget that we have to delete the MassDataIntegratorTask
            // allocated instance.
            mass_data_integrator_task_p->deleteLater();
            // Once the task has been labelled to be deleted later, we can stop
            // the thread and ask for it to also be deleted later.
            thread_p->deleteLater(), thread_p->quit();
            thread_p->wait();
          });

  // Ensure the mass data integrator messages are used.

  connect(
    mass_data_integrator_p,
    &QualifiedMassSpectrumVectorMassDataIntegrator::logTextToConsoleSignal,
    this,
    &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    mp_taskMonitorWnd->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to XIC chromatogram from the table view");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the MsRunReadTask instance has finished working, it will send a signal
  // that we trap to finally destroy (after a time lag of some seconds, the
  // monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(
            QualifiedMassSpectrumVectorMassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &QualifiedMassSpectrumVectorMassDataIntegrator::cancelOperation);

  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  emit integrateQualifiedMassSpectrumVectorToRtDtSignal(mass_data_integrator_p,
                                                        data_kind);

  // Because the emitter is the same ProgramWindow object, we need to disconnect
  // the signal so that at next opening of mass data file, it will not be called
  // twice.
  disconnect(
    this,

    static_cast<void (ProgramWindow::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *, pappso::DataKind)>(
      &ProgramWindow::integrateQualifiedMassSpectrumVectorToRtDtSignal),

    mass_data_integrator_task_p,

    static_cast<void (MassDataIntegratorTask::*)(
      QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *, pappso::DataKind)>(
      &MassDataIntegratorTask::integrateToRtDt));
}


void
ProgramWindow::finishedIntegratingQualifiedMassSpectrumVectorToRtDt(
  QualifiedMassSpectrumVectorMassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // At this point we need to ask the proper color map window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  // Get a pointer to the double maptrace map that will serve as the data to
  // fill-in the color map.
  std::shared_ptr<std::map<double, pappso::MapTrace>> double_map_trace_map_sp =
    static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *>(
      mass_data_integrator_p)
      ->getDoubleMapTraceMapSPtr();

  // qDebug() << "rt / mz map size:" << double_map_trace_map_sp->size();

  if(!double_map_trace_map_sp->size())
    {
      qDebug() << "There is not a single item in the rt / mz map.";
      return;
    }

  pappso::DataKind data_kind =
    static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *>(
      mass_data_integrator_p)
      ->getLastIntegrationDataKind();

  // We need to have the specifics of the color map: the number of m/z cells,
  // the minimum/maximum key value (the RT values)
  // the minimum/maximum mz value.

  QualifiedMassSpectrumVectorMassDataIntegratorToRtDt
    *qualified_mass_spectra_integrator =
      static_cast<QualifiedMassSpectrumVectorMassDataIntegratorToRtDt *>(
        mass_data_integrator_p);

  pappso::ColorMapPlotConfig color_map_plot_config(
    // Might be RT or DT
    data_kind,
    // Must be the other kind.
    (data_kind == pappso::DataKind::rt ? pappso::DataKind::dt
                                       : pappso::DataKind::rt),
    pappso::AxisScale::orig,
    pappso::AxisScale::orig,
    pappso::AxisScale::orig,
    qualified_mass_spectra_integrator->getColorMapKeyCellCount(),
    qualified_mass_spectra_integrator->getColorMapMzCellCount(),
    qualified_mass_spectra_integrator->getColorMapMinKey(),
    qualified_mass_spectra_integrator->getColorMapMaxKey(),
    qualified_mass_spectra_integrator->getColorMapMinMz(),
    qualified_mass_spectra_integrator->getColorMapMaxMz());

  // qDebug() << "color_map_plot_config:" << color_map_plot_config.toString();

  // Note that we do not support multiple color maps stacked one on top of the
  // other.

  mp_ticXicChromDriftSpecColorMapWnd->addColorMapPlot(double_map_trace_map_sp,
                                                      color_map_plot_config,
                                                      ms_run_data_set_csp,
                                                      processing_flow,
                                                      color,
                                                      nullptr);
  if(!mp_ticXicChromDriftSpecColorMapWnd->isVisible())
    mp_ticXicChromDriftSpecColorMapWnd->showWindow();

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}


void
ProgramWindow::xicIntegrationToRt(const ProcessingFlow &processing_flow)
{
  // qDebug().noquote() << "XIC integration to rt with processing flow:"
  //<< processing_flow.toString();

  // Sanity check:

  if(processing_flow.getMsRunDataSetCstSPtr() == nullptr)
    qFatal("Cannot be that the pointer is nullptr.");

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    processing_flow.getMsRunDataSetCstSPtr();

  // Pass the integrator the flow we got as param and that describes in its most
  // recent step the integration that it should perform.
  MsRunDataSetTreeMassDataIntegratorToRt *mass_data_integrator_p =
    new MsRunDataSetTreeMassDataIntegratorToRt(ms_run_data_set_csp,
                                               processing_flow);

  // Ensure the mass data integrator messages are used.

  connect(mass_data_integrator_p,
          &MassDataIntegrator::logTextToConsoleSignal,
          this,
          &ProgramWindow::logTextToConsole);

  // qDebug() << "the integrator pointer:" << mass_data_integrator_p;

  // Allocate a mass data integrator to integrate the data.

  MassDataIntegratorTask *mass_data_integrator_task_p =
    new MassDataIntegratorTask();

  // This signal starts the computation in the MassDataIntegratorTask object.
  connect(
    this,
    // SIGNAL(integrateToRtSignal(MsRunDataSetTreeMassDataIntegratorToRt *)),
    static_cast<void (ProgramWindow::*)(
      MsRunDataSetTreeMassDataIntegratorToRt *)>(
      &ProgramWindow::integrateToRtSignal),
    mass_data_integrator_task_p,
    // SLOT(integrateToRt(MsRunDataSetTreeMassDataIntegratorToRt *)),
    static_cast<void (MassDataIntegratorTask::*)(
      MsRunDataSetTreeMassDataIntegratorToRt *)>(
      &MassDataIntegratorTask::integrateToRt),
    // Fundamental for signals that travel across QThread instances...
    Qt::QueuedConnection);

  // Allocate the thread in which the integrator task will run.
  QThread *thread_p = new QThread;

  // Move the task to the matching thread.
  mass_data_integrator_task_p->moveToThread(thread_p);
  thread_p->start();

  // When the read task finishes, it sends a signal that we trap to go on with
  // the plot widget creation stuff.

  // Since we allocated the QThread dynamically we need to be able to destroy it
  // later, so make the connection.
  connect(
    mass_data_integrator_task_p,
    static_cast<void (MassDataIntegratorTask::*)(MassDataIntegrator *)>(
      &MassDataIntegratorTask::finishedIntegratingDataSignal),
    this,
    [this, thread_p, mass_data_integrator_p, mass_data_integrator_task_p]() {
      // Do not forget that we have to delete the MassDataIntegratorTask
      // allocated instance.
      mass_data_integrator_task_p->deleteLater();
      // Once the task has been labelled to be deleted later, we can stop
      // the thread and ask for it to also be deleted later.
      thread_p->deleteLater(), thread_p->quit();
      thread_p->wait();
      this->finishedXicIntegrationToRt(mass_data_integrator_p);
    });


  // Allocate a new TaskMonitorCompositeWidget that will receive all the
  // integrator's signals and provide feedback to the user about the ongoing
  // integration.

  TaskMonitorCompositeWidget *task_monitor_composite_widget_p =
    getTaskMonitorWnd()->addTaskMonitorWidget(Qt::red);

  // Initialize the monitor composite widget's widgets and make all the
  // connections mass data integrator <--> widget.

  task_monitor_composite_widget_p->setMsRunIdText(
    ms_run_data_set_csp->getMsRunId()->getSampleName());
  task_monitor_composite_widget_p->setTaskDescriptionText(
    "Integrating to XIC chromatogram.");
  task_monitor_composite_widget_p->setProgressBarMinValue(0);

  // Make the connections

  // When the integrator task instance has finished working, it will send a
  // signal that we trap to finally destroy (after a time lag of some seconds,
  // the monitor widget.

  connect(mass_data_integrator_task_p,
          static_cast<void (MassDataIntegratorTask::*)(MassDataIntegrator *)>(
            &MassDataIntegratorTask::finishedIntegratingDataSignal),
          task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::taskFinished,
          Qt::QueuedConnection);

  // If the user clicks the cancel button, relay the signal to the loader.
  connect(task_monitor_composite_widget_p,
          &TaskMonitorCompositeWidget::cancelTaskSignal,
          mass_data_integrator_p,
          &MassDataIntegrator::cancelOperation);

  // We need to register the meta type for std::size_t because otherwise it
  // cannot be shipped though signals.

  qRegisterMetaType<std::size_t>("std::size_t");

  // Now make all the connections that will allow the integrator to provide
  // dynamic feedback to the user via the task monitor widget.
  task_monitor_composite_widget_p->makeMassDataIntegratorConnections(
    mass_data_integrator_p);

  // qDebug() << "going to emit integrateToRtSignal with mass "
  //"data integrator:"
  //<< mass_data_integrator_p;

  emit integrateToRtSignal(mass_data_integrator_p);

  // We do not want to make signal/slot calls more than once. This is because
  // one user might well trigger more than one integration from this window to a
  // mass spectrum. Thus we do not want that *this window be still connected to
  // the specific mass_data_integrator_task_p when a new integration is
  // triggered. We want the signal/slot pairs to be contained to specific
  // objects. Each MassSpecTracePlotWnd::integrateToMz() call must be contained
  // to a this/mass_data_integrator_task_p specific signal/slot pair.
  disconnect(
    this,
    // SIGNAL(integrateToRtSignal(MsRunDataSetTreeMassDataIntegratorToRt *)),
    static_cast<void (ProgramWindow::*)(
      MsRunDataSetTreeMassDataIntegratorToRt *)>(
      &ProgramWindow::integrateToRtSignal),
    mass_data_integrator_task_p,
    // SLOT(integrateToRt(MsRunDataSetTreeMassDataIntegratorToRt *)));
    static_cast<void (MassDataIntegratorTask::*)(
      MsRunDataSetTreeMassDataIntegratorToRt *)>(
      &MassDataIntegratorTask::integrateToRt));
}


void
ProgramWindow::finishedXicIntegrationToRt(
  MassDataIntegrator *mass_data_integrator_p)
{
  // qDebug();

  // At this point we need to ask the TIC XIC chromatogram window to display the
  // result of the compuation.

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    mass_data_integrator_p->getMsRunDataSet();

  pappso::Trace trace = mass_data_integrator_p->getMapTrace().toTrace();

  ProcessingFlow processing_flow = mass_data_integrator_p->getProcessingFlow();

  QColor color =
    mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);

  mp_ticXicChromPlotWnd->newTrace(
    trace, ms_run_data_set_csp, processing_flow, color, nullptr);

  if(!mp_ticXicChromPlotWnd->isVisible())
    mp_ticXicChromPlotWnd->showWindow();

  // Finally destroy the integrator!
  delete mass_data_integrator_p;
}

void
ProgramWindow::msRunDataSetRemovalRequested(
  MsRunDataSetCstSPtr &ms_run_data_set_csp)
{
  // qDebug() << "Removal of ms run data set:" << ms_run_data_set_csp.get()
  //<< "requested";

  // The user asks that a given ms run data set be removed from the software.
  // We need to orderly remove all the shared pointer references so that the
  // object is destroyed. Shared pointers to the ms run data set are located
  // in the plottables created on the basis of the ms run data set. We need to
  // remove from all the plot widgets, all the plottables that have been
  // created using the ms run data set as the starting point of integrations.

  // All the plottables are registered in the tree that mimicks the structure
  // of all the plottable and plot widget in the program.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // Find all the root nodes that match ms_run_data_set_csp non recursively.
  std::vector<DataPlottableNode *> found_nodes =
    m_dataPlottableTree.findNodes(ms_run_data_set_csp, false);

  // qDebug() << "Found nodes:" << found_nodes.size() << "nodes:";
  // for(std::size_t iter = 0; iter < found_nodes.size(); ++iter)
  // qDebug() << found_nodes.at(iter)->toString(0, true);

  // Since each node has both the plot widget and the plottable pointer, we
  // can ask for the recursive destruction of the plottable.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  for(auto &&node : found_nodes)
    {
      // true: recursively
      // false: not destroying a widget.

      plottableDestructionRequested(
        node->getPlotWidget(), node->getPlottable(), true);
    }

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // Look for a tree view window that matches the ms
  // run data set.

  std::map<MsRunDataSetCstSPtr, MsRunDataSetTableViewWnd *>::iterator
    res_iterator = std::find_if(
      mp_msRunDataSetTableViewWndMap.begin(),
      mp_msRunDataSetTableViewWndMap.end(),
      [ms_run_data_set_csp](
        const std::pair<MsRunDataSetCstSPtr, MsRunDataSetTableViewWnd *> item) {
        return item.first == ms_run_data_set_csp;
      });

  if(res_iterator == mp_msRunDataSetTableViewWndMap.end())
    {
      // Nothing to do, the data set had not associated tree view.
    }
  else
    {
      delete res_iterator->second;

      mp_msRunDataSetTableViewWndMap.erase(ms_run_data_set_csp);
    }

  // Finally remove the ms run data set item from the list widget in the
  // open ms run data set dialog window.

  mp_openMsRunDataSetsDlg->removeMsRunDataSet(ms_run_data_set_csp);

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();
}


std::vector<MsRunDataSetCstSPtr>
ProgramWindow::allSelectedOrUniqueMsRunDataSet()
{
  return mp_openMsRunDataSetsDlg->allSelectedOrUniqueMsRunDataSets();
}


const DataPlottableTree &
ProgramWindow::getDataPlottableTree() const
{
  return m_dataPlottableTree;
}


void
ProgramWindow::showOpenMsRunDataSetsWnd()
{
  mp_openMsRunDataSetsDlg->activateWindow();
  mp_openMsRunDataSetsDlg->raise();
  mp_openMsRunDataSetsDlg->show();
}


const OpenMsRunDataSetsDlg *
ProgramWindow::getOpenMsRunDataSetsDlg() const
{
  return mp_openMsRunDataSetsDlg;
}


void
ProgramWindow::showTicXicChromatogramsWnd()
{
  mp_ticXicChromPlotWnd->showWindow();
}


void
ProgramWindow::showMassSpectraWnd()
{
  mp_massSpecPlotWnd->showWindow();
}


void
ProgramWindow::showDriftSpectraWnd()
{
  mp_driftSpecPlotWnd->showWindow();
}


void
ProgramWindow::showTicXicChromMassSpecColorMapWnd()
{
  mp_ticXicChromMassSpecColorMapWnd->showWindow();
}


void
ProgramWindow::showTicXicChromDriftSpecColorMapWnd()
{
  mp_ticXicChromDriftSpecColorMapWnd->showWindow();
}


void
ProgramWindow::showDriftSpecMassSpecColorMapWnd()
{
  mp_driftSpecMassSpecColorMapWnd->showWindow();
}


void
ProgramWindow::showXicExtractionWnd()
{

  mp_xicExtractionWnd->showWindow();
}


void
ProgramWindow::showIsoSpecDlg()
{
  mp_isoSpecDlg = new IsoSpecDlg(this, m_moduleName);

  mp_isoSpecDlg->activateWindow();
  mp_isoSpecDlg->raise();
  mp_isoSpecDlg->show();
}


void
ProgramWindow::showMassPeakShaperDlg()
{
  mp_massPeakShaperDlg = new MassPeakShaperDlg(this, m_moduleName);

  mp_massPeakShaperDlg->activateWindow();
  mp_massPeakShaperDlg->raise();
  mp_massPeakShaperDlg->show();
}


void
ProgramWindow::showTaskMonitorWnd()
{
  mp_taskMonitorWnd->activateWindow();
  mp_taskMonitorWnd->raise();
  mp_taskMonitorWnd->show();
}


void
ProgramWindow::showConsoleWnd()
{
  mp_consoleWnd->activateWindow();
  mp_consoleWnd->raise();
  mp_consoleWnd->show();
}


void
ProgramWindow::saveWorkspace()
{
  mp_openMsRunDataSetsDlg->writeSettings();
  mp_taskMonitorWnd->writeSettings();
  mp_consoleWnd->writeSettings();

  mp_ticXicChromPlotWnd->writeSettings();
  mp_massSpecPlotWnd->writeSettings();
  mp_driftSpecPlotWnd->writeSettings();
  mp_ticXicChromMassSpecColorMapWnd->writeSettings();
  mp_ticXicChromDriftSpecColorMapWnd->writeSettings();
  mp_driftSpecMassSpecColorMapWnd->writeSettings();

  mp_xicExtractionWnd->writeSettings();
}


void
ProgramWindow::logTextToConsole(QString msg)
{
  mp_consoleWnd->logTextToConsole(msg);
}


void
ProgramWindow::logColoredTextToConsole(QString text, const QColor &color)
{
  mp_consoleWnd->logColoredTextToConsole(text, color);
}


QColor
ProgramWindow::getColorForMsRunDataSet(
  MsRunDataSetCstSPtr ms_run_data_set_csp) const
{
  return mp_openMsRunDataSetsDlg->colorForMsRunDataSet(ms_run_data_set_csp);
}


//! Handler of the close event signal.
void
ProgramWindow::closeEvent(QCloseEvent *event)
{
  writeSettings();
  event->accept();
}


int
ProgramWindow::selectMsRun(std::vector<pappso::MsRunIdCstSPtr> &ms_run_ids)
{
  MsRunSelectorDlg *dlg = new MsRunSelectorDlg(this, ms_run_ids, m_moduleName);

  return dlg->exec();
}


BaseTracePlotWnd *
ProgramWindow::getPlotWndPtr(const QString &type_name)
{
  if(type_name != "mass spectrum" && type_name != "tic xic chromatogram" &&
     type_name != "drift spectrum")
    qFatal("Error with the plot window type name.");

  if(type_name == "mass spectrum")
    return mp_massSpecPlotWnd;
  else if(type_name == "tic xic chromatogram")
    return mp_ticXicChromPlotWnd;
  else if(type_name == "drift spectrum")
    return mp_driftSpecPlotWnd;

  qFatal("Should never encounter this point.");

  return nullptr;
}


void
ProgramWindow::integrateToRt(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  // Each trace plot window handles the integration requests that produce
  // their own type of data, so we delegate the integration to the
  // relevant window.

  // qDebug().noquote() << "Integrating to rt with processing flow:"
  //<< processing_flow.toString();

  mp_ticXicChromPlotWnd->integrateToRt(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


void
ProgramWindow::integrateToMz(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  // Each trace plot window handles the integration requests that produce
  // their own type of data, so we delegate the integration to the
  // relevant window.

  // qDebug().noquote() << "Integrating to mz with processing flow:"
  //<< processing_flow.toString();

  mp_massSpecPlotWnd->integrateToMz(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


void
ProgramWindow::integrateToDt(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  // Each trace plot window handles the integration requests that produce
  // their own type of data, so we delegate the integration to the
  // relevant window.

  mp_driftSpecPlotWnd->integrateToDt(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


void
ProgramWindow::integrateToDtMz(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  mp_driftSpecMassSpecColorMapWnd->integrateToDtMz(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


void
ProgramWindow::integrateToRtMz(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  mp_ticXicChromMassSpecColorMapWnd->integrateToRtMz(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


void
ProgramWindow::integrateToRtDt(
  QCPAbstractPlottable *parent_plottable_p,
  std::shared_ptr<std::vector<pappso::QualifiedMassSpectrumCstSPtr>>
    qualified_mass_spectra_sp,
  const ProcessingFlow &processing_flow)
{
  mp_ticXicChromDriftSpecColorMapWnd->integrateToRtDt(
    parent_plottable_p, qualified_mass_spectra_sp, processing_flow);
}


const BasePlotCompositeWidget *
ProgramWindow::getPlotWidget(
  [[maybe_unused]] MsRunDataSetCstSPtr ms_run_data_set_csp,
  QCPAbstractPlottable *plottable_p)
{
  return m_dataPlottableTree.getPlotWidget(plottable_p);
}


void
ProgramWindow::documentMsRunDataPlottableFiliation(
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  QCPAbstractPlottable *new_plottable_p,
  QCPAbstractPlottable *parent_plottable_p,
  BasePlotCompositeWidget *plot_widget_p)
{
  // qDebug();

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // Now we need to document the filiation of the parent / child
  // plottable.

  // qDebug() << "Before documenting: data plottable tree has"
  //<< m_dataPlottableTree.depth() << "depth and"
  //<< m_dataPlottableTree.size() << "size";

  m_dataPlottableTree.addPlottable(
    ms_run_data_set_csp, new_plottable_p, parent_plottable_p, plot_widget_p);

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // qDebug() << "After documenting data plot plottable tree has"
  //<< m_dataPlottableTree.depth() << "depth and"
  //<< m_dataPlottableTree.size() << "size";
}


void
ProgramWindow::plottableDestructionRequested(
  BasePlotCompositeWidget *base_plot_composite_widget_p,
  QCPAbstractPlottable *plottable_p,
  bool recursively)
{
  // qDebug().noquote() << "Destruction of plottable:"
  //<< pappso::Utils::pointerToString(plottable_p)
  //<< "is being requested recursively:" << recursively;

  MsRunDataSetCstSPtr ms_run_data_set_csp =
    base_plot_composite_widget_p->getMsRunDataSetCstSPtrForPlottable(
      plottable_p);

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // When one plottable is on the verge of being deleted, we need to first
  // check its node in the plottable data tree. This is because we need to
  // at least remove that node !

  // Further, there is another fact to keep in mind: if the user wanted to
  // remove all the children plottable, then we would need to do that.

  // Another complexity: if the plottable that is removed had children,
  // but the user does not want to remove them, we would need to reparent
  // them to the parent of the plottable being deleted !

  // How many children did that plottable have?

  // qDebug() << "Getting the right node for the plottable to be destroyed.";

  DataPlottableNode *plottable_node_p =
    m_dataPlottableTree.findNode(plottable_p);

  if(plottable_node_p == nullptr)
    qFatal(
      "Could not find a node for plottable . This is a programming "
      "error.");

  // qDebug() << "The plottable is in node:"
  //<< plottable_node_p->toString(0, true);

  std::size_t descendant_count = 0;
  plottable_node_p->size(descendant_count);

  // qDebug() << "The plottable node has" << descendant_count <<
  // "descendants";

  // bool res =
  m_dataPlottableTree.removeNodeAndPlottable(plottable_node_p, recursively);

  // qDebug() << "Removal of node(s) and plottable(s) is success:" << res;

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  //  qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();
}


void
ProgramWindow::plottableDestructionRequested(
  BasePlotCompositeWidget *base_plot_composite_widget_p,
  QCPAbstractPlottable *plottable_p,
  const pappso::BasePlotContext &context)
{
  bool recursively = context.keyboardModifiers & Qt::ControlModifier;

  // If a recursive plottable destruction is requested, the the plottable
  // and all its children, as documented by the main program's plottable
  // tree nodes are to be destroyed.

  // if(recursively)
  // qDebug() << "Destruction recursive of plottable:" << plottable_p
  //<< "is being requested.";
  // else
  // qDebug() << "Destruction of plottable:" << plottable_p
  //<< "is being requested.";

  return plottableDestructionRequested(
    base_plot_composite_widget_p, plottable_p, recursively);
}


void
ProgramWindow::plotCompositeWidgetDestructionRequested(
  BasePlotCompositeWidget *base_plot_composite_widget_p)
{
  // qDebug() << "Handling the destruction of the plot widget:"
  //<< base_plot_composite_widget_p;

  // One plot widget and all the plottables that it contains need to be
  // destroyed. We first need to get a list of all the data plot tree nodes
  // that match the plot widget. Then we non-recursively destroy all the
  // plottables and matching data tree nodes. Finally we destroy the widget.

  std::vector<DataPlottableNode *> plottable_nodes =
    m_dataPlottableTree.findNodes(base_plot_composite_widget_p, true);

  // qDebug() << "The number of nodes for plot widget:"
  //<< base_plot_composite_widget_p << "is:" << plottable_nodes.size();

  // We want an orderly destruction of the plottable(s).

  for(auto &&node_p : plottable_nodes)
    {

      plottableDestructionRequested(
        base_plot_composite_widget_p, node_p->getPlottable(), false);
    }

  // Once all the plottables have been destroyed, we need to destroy the plot
  // widget itself.

  delete base_plot_composite_widget_p;

  // qDebug() << "Done deleting the composite widget.";
}


void
ProgramWindow::showMsRunDataSetTableViewWnd(
  MsRunDataSetCstSPtr ms_run_data_set_csp, const QColor &color)
{
  // qDebug();

  // Each ms run data set has the ability to show its data in the form of a
  // tree view in a specific window. This is what we want to do here.

  // Let's see the usage count of the MsRunDataSetCstSPtr.
  // qDebug() << "Usage count:" << ms_run_data_set_csp.use_count();

  // First check if the window exists already.

  std::map<MsRunDataSetCstSPtr, MsRunDataSetTableViewWnd *>::iterator
    res_iterator = std::find_if(
      mp_msRunDataSetTableViewWndMap.begin(),
      mp_msRunDataSetTableViewWndMap.end(),
      [ms_run_data_set_csp](
        const std::pair<MsRunDataSetCstSPtr, MsRunDataSetTableViewWnd *> item) {
        return item.first == ms_run_data_set_csp;
      });

  MsRunDataSetTableViewWnd *tree_view_wnd_p = nullptr;

  if(res_iterator == mp_msRunDataSetTableViewWndMap.end())
    {
      // The tree view window for this ms run data set was never asked to be
      // shown. Thus, do the whole work.

      tree_view_wnd_p =
        new MsRunDataSetTableViewWnd(this, this, ms_run_data_set_csp, color);

      // qDebug() << "Creating new ms run data set tree view.";

      // Immediately map the ms run data set and the tree view.

      mp_msRunDataSetTableViewWndMap[ms_run_data_set_csp] = tree_view_wnd_p;
    }
  else
    {
      // qDebug() << "A map item was found;";

      tree_view_wnd_p = res_iterator->second;
    }

  tree_view_wnd_p->activateWindow();
  tree_view_wnd_p->raise();
  tree_view_wnd_p->show();
}

} // namespace minexpert

} // namespace msxps
