/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


////////////////////////////// Qt includes
#include <QStringList>
#include <QFile>
#include <QRegularExpression>
#include <QDebug>
#include <QVector>
#include <QCoreApplication>

////////////////////////////// local includes
#include <minexpert/nongui/MassSpecDataFileLoaderDx.hpp>
#include <minexpert/nongui/globals.hpp>


namespace msXpSmineXpert
{

MassSpecDataFileLoaderDx::MassSpecDataFileLoaderDx(const QString &fileName)
  : MassSpecDataFileLoader{fileName}
{
}


MassSpecDataFileLoaderDx::~MassSpecDataFileLoaderDx()
{
}


QString
MassSpecDataFileLoaderDx::formatDescription() const
{
  QString description = QString(
    "The jcamp-dx file format is the\n"
    "The joint Committee on Atomic and Molecular Physical data – Data "
    "Exchange format.\n"
    "The file has a header with each line starting with the \"##\" tokens.\n"
    "The mass data are organized by spectrum of which the mz,i pairs \n"
    "are formatted like the following: \n"
    "<number>,<number>;<number>,<number>;<number>,<number>;...;\n"
    "Note how the mass data for a single spectrum ends with \';\'.\n"
    "This file format may hold data for any number of  mass spectra.\n");

  return description;
}


void
MassSpecDataFileLoaderDx::cancelOperation()
{
  m_isOperationCancelled = true;

  qDebug() << __FILE__ << __LINE__ << "MzmlFileParser::cancelOperation";
}


int
MassSpecDataFileLoaderDx::readSpectrumCount()
{
  // The dx file as produced by Bruker CompassXport has this kind of header:
  //
  //##TITLE= ANALYSIS.BAF
  //##JCAMP-DX= 6.00
  //##DATA TYPE= MASS SPECTRUM
  //##DATA CLASS= NTUPLES
  //##ORIGIN=  DA_YC
  //##OWNER=
  //##LONGDATE= 201:/06/17 15:22:41.531 +01:00
  //##SOURCE REFERENCE=
  // C:\Users\polipo\Desktop\marie-erard-massdata\20140617-rusconi-erard-gfp-hocl-4um-2min-dessalee.d\ANALYSIS.BAF
  //##SAMPLE DESCRIPTION=
  //##DATA PROCESSING= RECALIBRATION,  ,
  //##SPECTROMETER/DATA SYSTEM= microTOFLC Q
  //##NTUPLES= MASS SPECTRUM
  //##VAR_NAME= MASS, INTENSITY, RETENTION TIME
  //##SYMBOL= X, Y, T
  //##SYMBOL= INDEPENDENT, DEPENDENT, INDEPENDENT
  //##VAR_FORM= AFFN, AFFN, AFFN
  //##VAR_DIM= , , 101
  //##UNITS= M/Z, RELATIVE ABUNDANCE, SECONDS
  //##FIRST= 1.158000
  //##LAST= 101.538000

  // And then there are all the spectra with this header for each spectrum:
  //
  //##PAGE= T= 1.158000
  //##NPOINTS= 7466
  //##DATA TABLE= (XY..XY), PEAKS
  // 694.642704,106.000000;694.759649,92.000000;694.896391,131.000000;....
  // the line above is a single very long line with all the mz,i pairs
  // separated by ';' without spaces.
  //
  // Then, the next spectrum:
  //
  //##PAGE= T= 2.162000
  //##NPOINTS= 7400
  //##DATA TABLE= (XY..XY), PEAKS
  // 694.637238,77.000000;694.763933,81.000000;694.914571,112.000000;
  // and so on.

  // So the point here is to parse the whole file and count the number of
  // items containing "PAGE= "
  // Note that the T= <value> is the elution time in seconds.

  // Reinit the spectrum count.
  m_spectrumCount = 0;

  if(m_fileName.isEmpty())
    {
      qDebug() << __FILE__ << __LINE__ << "fileName is empty";

      return -1;
    }

  QFile file(m_fileName);

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      qDebug() << __FILE__ << __LINE__ << "Failed to open file" << m_fileName;

      return -1;
    }

  QRegularExpression pageTime("##PAGE= T= ");
  QRegularExpressionMatch match;

  QString errorMsg;

  while(!file.atEnd())
    {
      QString line = file.readLine();

      if(line.isEmpty())
        continue;

      // Does the line match the page and elution time regexp?
      match = pageTime.match(line);
      if(match.hasMatch())
        m_spectrumCount++;
    }

  file.close();

  return m_spectrumCount;
}


bool
MassSpecDataFileLoaderDx::canLoadData(QString fileName)
{
  int LIMIT_NUMBER_LINE_TO_READ = 50;

  QFile file(fileName);

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      qDebug() << __FILE__ << __LINE__ << "Failed to open file" << fileName;

      return -1;
    }

  bool titleLineFound    = false;
  bool formatLineFound   = false;
  bool dataTypeLineFound = false;

  int count = 0;

  QString line = file.readLine();

  while(line.startsWith("##"))
    {
      if(line.contains("##TITLE="))
        titleLineFound = true;
      else if(line.contains("##JCAMP-DX="))
        formatLineFound = true;
      else if(line.contains("##DATA TYPE="))
        dataTypeLineFound = true;

      if(++count > LIMIT_NUMBER_LINE_TO_READ)
        break;

      line = file.readLine();
    }

  if(titleLineFound && formatLineFound && dataTypeLineFound)
    return true;
  else
    return false;
}


msXpSlibmass::MassSpectrum *
MassSpecDataFileLoaderDx::parseSpectrum(QString line)
{
  if(line.isEmpty())
    return 0;

  // The line is in the format:
  // 694.499324,35.000000;694.516080,44.000000;... goes on and on in a single
  // line of mz,i pairs.
  // Note that this line ends with ';\n'.

  msXpSlibmass::MassSpectrum *massSpectrum = new msXpSlibmass::MassSpectrum;

  int lineLength        = line.length();
  int prevSemicolumnIdx = 0;
  int newSemicolumnIdx  = line.indexOf(';', prevSemicolumnIdx);

  bool ok = false;

  // We need to test the new index with respect to the end of line -1 chars
  // because the line ends with ';' and then '\n'.

  while(newSemicolumnIdx != -1 && newSemicolumnIdx < lineLength - 1)
    {
      // Get the string length that is contained between new and prev.

      int charCount = newSemicolumnIdx - prevSemicolumnIdx - 1;

      QString mzI = line.mid(prevSemicolumnIdx, charCount);

      // qDebug() << __FILE__ << __LINE__
      //<< "prevSemicolumnIdx: " << prevSemicolumnIdx << "/"
      //<< "newSemicolumnIdx" << newSemicolumnIdx
      //<< "and mzI is:" << mzI;

      // Split the comma-separated mz and i values.

      QStringList mzIstringList = mzI.split(',');

      double mz = mzIstringList.at(0).toDouble(&ok);

      if(!ok)
        {
          qDebug() << __FILE__ << __LINE__ << "Failed to convert mz"
                   << mzIstringList.at(0) << "to double.";

          delete massSpectrum;
          return nullptr;
        }

      double i = mzIstringList.at(1).toDouble(&ok);

      if(!ok)
        {
          qDebug() << __FILE__ << __LINE__ << "Failed to convert i"
                   << mzIstringList.at(1) << "to double.";

          delete massSpectrum;
          return nullptr;
        }

      // At this point we know that we have proper mz and i values. Let's
      // process these by creating a mass peak that we can append to the
      // spectrum that we allocated at the beginning of this function.

      msXpSlibmass::DataPoint *massPeak = new msXpSlibmass::DataPoint(mz, i);
      massSpectrum->append(massPeak);

      // qDebug() << __FILE__ << __LINE__ << "Appending new massPeak for
      // string"
      //<< mzI;


      // Go on with the moving window to the next ;-separated mz,i pair.

      prevSemicolumnIdx = newSemicolumnIdx + 1;

      // Actutally get the new line mzI coordinates.

      newSemicolumnIdx = line.indexOf(';', prevSemicolumnIdx);

      // qDebug() << __FILE__ << __LINE__
      //<< "prevSemicolumnIdx: " << prevSemicolumnIdx << "/"
      //<< "newSemicolumnIdx" << newSemicolumnIdx << "with"
      //<< "charCount" << charCount << "and"
      //<< "mzI:" << mzI;
    }

  return massSpectrum;
}


int
MassSpecDataFileLoaderDx::loadData(MassSpecDataSet *massSpecDataSet)
{

  // Will set the value to m_spectrumCount.

  readSpectrumCount();

  if(m_spectrumCount == 0)
    {
      qDebug() << __FILE__ << __LINE__ << "No spectrum to load.";

      return 0;
    }

  // qDebug() << __FILE__ << __LINE__ << "spectrumCount:" << m_spectrumCount;

  if(massSpecDataSet == nullptr)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  QFile file(m_fileName);

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      qDebug() << __FILE__ << __LINE__ << "Failed to open file" << m_fileName;

      return -1;
    }

  // Then, each spectrum has a header like this and then the data:

  //##PAGE= T= 1.158000
  //##NPOINTS= 74528
  //##DATA TABLE= (XY..XY), PEAKS
  // 694.499324,35.000000;694.516080,44.000000;... goes on and on in a single
  // line of mz,i pairs.

  // Craft the proper regular expression for the mass data:

  QRegularExpression endOfLine("^\\s+$");
  QRegularExpression pageTimeLine("^##PAGE= T= (\\d*\\.?\\d*)\\s*$");
  QRegularExpression numberPointsLine("^##NPOINTS= (\\d+)\\s*$");
  QRegularExpression dataTablePeaksLine(
    "^##DATA TABLE= \\(XY..XY\\), PEAKS\\s*$");
  QRegularExpression realDataLine(
    "(\\d*\\.?\\d*)([\\s,]*)(-?\\d*\\.?\\d*e?\\d*);");

  QRegularExpressionMatch match;

  int spectraRead  = 0;
  double rt        = 0.0;
  int numberPoints = 0;

  bool dataTablePeaksLineWasLast = false;

  bool ok = false;

  while(!file.atEnd())
    {
      QCoreApplication::processEvents();

      if(m_isOperationCancelled)
        break;

      QString line = file.readLine();
      // qDebug() << __FILE__ << __LINE__
      //<< "line is: " << line;

      if(line.isEmpty())
        {
          dataTablePeaksLineWasLast = false;

          // qDebug() << __FILE__ << __LINE__
          //<< "line is empty.";

          continue;
        }

      // If the line has only a newline character, go on.
      match = endOfLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the end of line.";

          continue;
        }

      match = pageTimeLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // Get the retention time of the current spectrum.
          rt = match.captured(1).toDouble(&ok);
          if(!ok)
            {
              qDebug() << __FILE__ << __LINE__ << "Failed to convert"
                       << match.captured(1) << "to double";

              return -1;
            }

          // qDebug() << __FILE__ << __LINE__ << "rt:" << rt;
        }

      match = numberPointsLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // Get the number of points of the current spectrum.
          numberPoints = match.captured(1).toInt(&ok);
          if(!ok)
            {
              qDebug() << __FILE__ << __LINE__ << "Failed to convert"
                       << match.captured(1) << "to int";

              return -1;
            }

          // qDebug() << __FILE__ << __LINE__ << "numberPoints:" <<
          // numberPoints;
        }

      match = dataTablePeaksLine.match(line);

      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = true;

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the DATA TABLE PEAKS line.";

          continue;
        }

      if(line.startsWith("##"))
        {
          dataTablePeaksLineWasLast = false;

          continue;
        }

      // If previous line was the data table peaks line, then this one should
      // be the data. Or there is an error.

      // Format of the numerical string:
      // 694.642704,106.000000;694.759649,92.000000;\n

      match = realDataLine.match(line);

      if(!match.hasMatch())
        {
          if(dataTablePeaksLineWasLast)
            {
              qDebug() << __FILE__ << __LINE__ << "File " << m_fileName
                       << "is not a dx file";

              return false;
            }
        }
      else // was: if(!match.hasMatch())
        {
          if(!dataTablePeaksLineWasLast)
            {
              qDebug() << __FILE__ << __LINE__ << "File " << m_fileName
                       << "is not a dx file";

              return false;
            }

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the realData with strings:"
          //<< match.captured(1) << "and" << match.captured(3);

          msXpSlibmass::MassSpectrum *newSpectrum = parseSpectrum(line);

          if(newSpectrum == nullptr)
            qFatal(
              "Fatal error at %s@%d. Failed to parse spectrum. Program "
              "aborted.",
              __FILE__,
              __LINE__);

          if(newSpectrum->size() != numberPoints)
            {
              qDebug()
                << __FILE__ << __LINE__
                << "Failed to parse the proper number of points in spectrum"
                << "for retention time" << rt << "--" << newSpectrum->size()
                << "is different than" << numberPoints;

              qFatal(
                "Fatal error at %s@%d. Failed to parse spectrum. "
                "Program aborted.",
                __FILE__,
                __LINE__);

              return -1;
            }


          // Do not forget to set that retention time to the spectrum
          // itself !

          newSpectrum->rrt() = rt;

          if(massSpecDataSet->isStreamed())
            {
              // We are reading in streamed mode. This is the chance we
              // have to compute the TIC for the spectrum:

              double newTic = newSpectrum->valSum();

              // Had already another spectrum have the same rt ? If so
              // another TIC was already computed.

              double oldTic = massSpecDataSet->m_ticChromMap.value(rt, qSNaN());

              if(!qIsNaN(oldTic))
                {

                  // A rt by the same value alreay existed, update the
                  // tic value and insert it, that will erase the old
                  // value, because the map is NOT a multimap.

                  newTic += oldTic;
                }

              // And now store the key/value pair for later to use to
              // plot the TIC chromatogram.

              massSpecDataSet->m_ticChromMap.insert(rt, newTic);

              // Now delete the mass spectrum, that we won't use anymore.
              delete newSpectrum;
            }
          else
            {
              massSpecDataSet->appendMassSpectrum(newSpectrum);
              massSpecDataSet->insertRtHash(rt, newSpectrum);
            }
        }
      // End of
      // else // was: if(!match.hasMatch())

      // At this point we can increment the number of spectra that were
      // effectively read.
      ++spectraRead;

      QString msg = QString("Parsed spectrum number %1\n").arg(spectraRead);

      emit updateFeedbackSignal(
        msg, spectraRead, true /* setVisible */, LogType::LOG_TO_BOTH);
    }

  // We may be here because the operation was cancelled. Of course, in that
  // case we have not read all the spectra, but it is not an error, the user
  // knows he has cancelled the operation.

  if(!m_isOperationCancelled)
    {
      if(spectraRead != m_spectrumCount)
        {
          qDebug() << __FILE__ << __LINE__
                   << "The number of parsed spectra does "
                      "not match the previous analysis "
                      "of the file.";


          qFatal("Fatal error at %s@%d. Failed to parse file. Program aborted.",
                 __FILE__,
                 __LINE__);
        }

      // Sanity check:
      if(!massSpecDataSet->isStreamed())
        {
          if(massSpecDataSet->m_massSpectra.size() != spectraRead)
            {
              qDebug() << __FILE__ << __LINE__
                       << "The number of parsed spectra does "
                          "not match the previous analysis "
                          "of the file.";


              qFatal(
                "Fatal error at %s@%d. Failed to parse file. Program "
                "aborted.",
                __FILE__,
                __LINE__);
            }
        }
    }

  file.close();

  return spectraRead;
}


int
MassSpecDataFileLoaderDx::streamedIntegration(const History &history,
                                              QVector<double> *keyVector,
                                              QVector<double> *valVector,
                                              double *intensity)
{
  if(keyVector == Q_NULLPTR && valVector == Q_NULLPTR && intensity == Q_NULLPTR)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  if(intensity == Q_NULLPTR)
    {
      if(keyVector == Q_NULLPTR || valVector == Q_NULLPTR)
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);
    }
  else
    {
      if(keyVector != Q_NULLPTR || valVector != Q_NULLPTR)
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);
    }

  if(!history.isValid())
    {
      qDebug() << __FILE__ << __LINE__
               << "The History is not valid. Returning.";

      return -1;
    }

  // We are asked to compute a mass spectrum on the basis of what is in
  // the history parameter. Let's craft a SQL query string, perform the
  // query and then compute the TIC chromatogram, of which time is the key
  // and count is the value, returned in the keyVector and valVector
  // parameters.

  // At this point, we need to make sure we have all the rt/dt
  // integrations so that we can take them into account while doing the
  // SELECT operations for the database (m_db).

  // RT integration.
  // ===============
  bool rtIntegration = false;
  double rtStart     = -1;
  double rtEnd       = -1;
  rtIntegration      = history.innermostRtRange(&rtStart, &rtEnd);

  if(!rtIntegration)
    qFatal(
      "Fatal error at %s@%d. Cannot integration over RT with no"
      "integration specifications overs RT. Program aborted.",
      __FILE__,
      __LINE__);


  qDebug() << __FILE__ << __LINE__
           << "The integration is asked between RT range [" << rtStart << "-"
           << rtEnd << "]";

  // We need to iterate in the file and interrogate the various
  //##PAGE= T= 1.158000 lines to extract only the spectra matching the
  // integration rt range.

  // Before going on with the actual results parsing, let's setup the
  // feedback routine. Note the min = 0 and max = 0 values because we cannot
  // determine the count of spectra to load. Simply provide a moving
  // feedback.

  QString msgSkeleton = "Computing streamed m/z integration";

  emit updateFeedbackSignal(
    msgSkeleton, 0, true /* setVisible */, LogType::LOG_TO_BOTH);

  QFile file(m_fileName);

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      qDebug() << __FILE__ << __LINE__ << "Failed to open file" << m_fileName;

      return -1;
    }

  // Then, each spectrum has a header like this and then the data:

  //##PAGE= T= 1.158000
  //##NPOINTS= 74528
  //##DATA TABLE= (XY..XY), PEAKS
  // 694.499324,35.000000;694.516080,44.000000;... goes on and on in a single
  // line of mz,i pairs.

  // Craft the proper regular expression for the mass data:

  QRegularExpression endOfLine("^\\s+$");
  QRegularExpression pageTimeLine("^##PAGE= T= (\\d*\\.?\\d*)\\s*$");
  QRegularExpression numberPointsLine("^##NPOINTS= (\\d+)\\s*$");
  QRegularExpression dataTablePeaksLine(
    "^##DATA TABLE= \\(XY..XY\\), PEAKS\\s*$");
  QRegularExpression realDataLine(
    "(\\d*\\.?\\d*)([\\s,]*)(-?\\d*\\.?\\d*e?\\d*);");

  QRegularExpressionMatch match;

  msXpSlibmass::MassSpectrum combinedMassSpectrum;

  int spectraRead  = 0;
  double rt        = 0.0;
  int numberPoints = 0;

  bool dataTablePeaksLineWasLast = false;

  bool ok = false;

  while(!file.atEnd())
    {
      if(m_isOperationCancelled)
        break;

      QString line = file.readLine();
      // qDebug() << __FILE__ << __LINE__
      //<< "line is: " << line;

      if(line.isEmpty())
        {
          dataTablePeaksLineWasLast = false;

          // qDebug() << __FILE__ << __LINE__
          //<< "line is empty.";

          continue;
        }

      // If the line has only a newline character, go on.
      match = endOfLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the end of line.";

          continue;
        }

      match = pageTimeLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // Get the retention time of the current spectrum.
          rt = match.captured(1).toDouble(&ok);
          if(!ok)
            {
              qDebug() << __FILE__ << __LINE__ << "Failed to convert"
                       << match.captured(1) << "to double";

              return -1;
            }

          // This is the place where we should check that the data are
          // actually correct for the rt integration range.

          if(rt < rtStart)
            continue;
          if(rt > rtEnd)
            break;
        }

      // At this point we know we are in the target retention time interval.
      // Go on with the parsing.

      match = numberPointsLine.match(line);
      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = false;

          // Get the number of points of the current spectrum.
          numberPoints = match.captured(1).toInt(&ok);
          if(!ok)
            {
              qDebug() << __FILE__ << __LINE__ << "Failed to convert"
                       << match.captured(1) << "to int";

              return -1;
            }

          // qDebug() << __FILE__ << __LINE__ << "numberPoints:" <<
          // numberPoints;
        }

      match = dataTablePeaksLine.match(line);

      if(match.hasMatch())
        {
          dataTablePeaksLineWasLast = true;

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the DATA TABLE PEAKS line.";

          continue;
        }

      if(line.startsWith("##"))
        {
          dataTablePeaksLineWasLast = false;

          continue;
        }

      // If previous line was the data table peaks line, then this one should
      // be the data. Or there is an error.

      // Format of the numerical string:
      // 694.642704,106.000000;694.759649,92.000000;\n

      match = realDataLine.match(line);

      if(!match.hasMatch())
        {
          if(dataTablePeaksLineWasLast)
            {
              qDebug() << __FILE__ << __LINE__ << "File " << m_fileName
                       << "is not a dx file";

              return false;
            }
        }
      else // was: if(!match.hasMatch())
        {
          if(!dataTablePeaksLineWasLast)
            {
              qDebug() << __FILE__ << __LINE__ << "File " << m_fileName
                       << "is not a dx file";

              return false;
            }

          // qDebug() << __FILE__ << __LINE__
          //<< "matched the realData with strings:"
          //<< match.captured(1) << "and" << match.captured(3);

          msXpSlibmass::MassSpectrum *newSpectrum = parseSpectrum(line);

          if(newSpectrum == nullptr)
            qFatal(
              "Fatal error at %s@%d. Failed to parse spectrum. Program "
              "aborted.",
              __FILE__,
              __LINE__);

          if(newSpectrum->size() != numberPoints)
            {
              qDebug()
                << __FILE__ << __LINE__
                << "Failed to parse the proper number of points in spectrum"
                << "for retention time" << rt << "--" << newSpectrum->size()
                << "is different than" << numberPoints;

              qFatal(
                "Fatal error at %s@%d. Failed to parse spectrum. "
                "Program aborted.",
                __FILE__,
                __LINE__);

              return -1;
            }


          // Do not forget to set that retention time to the spectrum
          // itself !

          newSpectrum->rrt() = rt;

          // We are reading in streamed mode. We need to combine each new
          // spectrum to the previous ones.

          // qDebug() << __FILE__ << __LINE__ << "integrating for rt:" << rt;

          combinedMassSpectrum.combine(*newSpectrum);

          // At this point we can increment the number of spectra that were
          // effectively read.
          ++spectraRead;

          QString msg = QString(" -- spectrum number %1\n").arg(spectraRead);

          emit updateFeedbackSignal(msgSkeleton + msg,
                                    -1,
                                    true /* setVisible */,
                                    LogType::LOG_TO_STATUS_BAR);

          // qDebug() << __FILE__ << __LINE__ << msg;
        }
      // End of
      // else // was: if(!match.hasMatch())
    }
  // End of
  // while(!file.atEnd())

  if(intensity != Q_NULLPTR)
    *intensity = combinedMassSpectrum.valSum();
  else
    {
      *keyVector = QVector<double>::fromList(combinedMassSpectrum.keyList());
      *valVector = QVector<double>::fromList(combinedMassSpectrum.valList());
    }

  file.close();

  // qDebug() << __FILE__ << __LINE__ << "Mz streamed integration has parsed "
  //<< spectraRead << "spectra.";

  return spectraRead;
}


} // namespace msXpSmineXpert
