/**
 * \file pappsomspp/msrun/private/timsmsrunreader.h
 * \date 05/09/2019
 * \author Olivier Langella
 * \brief MSrun file reader for native Bruker TimsTOF raw data
 */

/*******************************************************************************
 * Copyright (c) 2019 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "timsframesmsrunreader.h"
#include "../../exception/exceptionnotimplemented.h"
#include <QDebug>

using namespace pappso;

TimsFramesMsRunReader::TimsFramesMsRunReader(MsRunIdCstSPtr &msrun_id_csp)
  : MsRunReader(msrun_id_csp)
{
  qDebug() << "Now initializing the TimsFramesMsRunReader.";

  initialize();
}

TimsFramesMsRunReader::~TimsFramesMsRunReader()
{
  msp_timsData = nullptr;
}

void
TimsFramesMsRunReader::initialize()
{
  msp_timsData = std::make_shared<TimsData>(mcsp_msRunId.get()->getFileName());
  if(msp_timsData == nullptr)
    {
      throw PappsoException(
        QObject::tr("ERROR in TimsFramesMsRunReader::initialize "
                    "msp_timsData is null for MsRunId %1")
          .arg(mcsp_msRunId.get()->toString()));
    }
}


bool
TimsFramesMsRunReader::accept(const QString &file_name) const
{
  qDebug() << file_name;
  return true;
}


pappso::MassSpectrumSPtr
TimsFramesMsRunReader::massSpectrumSPtr(
  [[maybe_unused]] std::size_t spectrum_index)
{
  throw ExceptionNotImplemented(
    QObject::tr("Not yet implemented in TimsFramesMsRunReader %1.\n")
      .arg(__LINE__));

  return pappso::MassSpectrumSPtr();
}


pappso::MassSpectrumCstSPtr
TimsFramesMsRunReader::massSpectrumCstSPtr(std::size_t spectrum_index)
{
  return msp_timsData->getMassSpectrumCstSPtrByRawIndex(spectrum_index);
}


QualifiedMassSpectrum
TimsFramesMsRunReader::qualifiedMassSpectrum(std::size_t spectrum_index,
                                             bool want_binary_data) const
{

  QualifiedMassSpectrum mass_spectrum;

  msp_timsData->getQualifiedMassSpectrumByRawIndex(
    getMsRunId(), mass_spectrum, spectrum_index, want_binary_data);
  return mass_spectrum;
}


void
TimsFramesMsRunReader::readSpectrumCollection(
  SpectrumCollectionHandlerInterface &handler)
{
  // Here we want to only handle mass spectra as combined scans spectra, that
  // is, for each frame, we want all the mobility scans to be cumulated (at the
  // integer level, that is, without conversion of all the integers to m/z
  // values) so as to yield a *single* mass spectrum per frame.

  // We can iterate in the vector of frames records and for each frame ask that
  // all its scans be combined into a single mass spectrum.

  const std::vector<FrameIdDescr> frame_id_descr_list =
    msp_timsData->getFrameIdDescrList();

  // The scan index is the index of the scan in the *whole* mass data file, it
  // is a sequential number of scan over all the frames.
  std::size_t scan_index = 0; // iterate in each spectrum

  // We'll need it to perform the looping in the spectrum list.
  std::size_t frames_count = msp_timsData->getTotalNumberOfFrames();

  qDebug() << "The spectrum list has this much frames:" << frames_count;

  // Inform the handler of the spectrum list so that it can handle feedback to
  // the user.
  handler.spectrumListHasSize(frames_count);

  for(const FrameIdDescr &current_frame : frame_id_descr_list)
    {
      TimsFrameCstSPtr tims_frame =
        msp_timsData->getTimsFrameCstSPtrCached(current_frame.m_frameId);

      // Get to know the size of the frame, that is, the number of mobility
      // scans in it.

      quint32 scan_count = tims_frame->getTotalNumberOfScans();

      Trace spectrum = tims_frame->cumulateScanToTrace(0, scan_count - 1);

      QualifiedMassSpectrum mass_spectrum;

      MassSpectrumId spectrum_id;

      spectrum_id.setSpectrumIndex(scan_index);

      spectrum_id.setMsRunId(getMsRunId());

      // Can modify to help our case
      spectrum_id.setNativeId(QString("frame=%1 index=%2")
                                .arg(current_frame.m_frameId)
                                .arg(scan_index));

      mass_spectrum.setMassSpectrumId(spectrum_id);

      // We want to document the retention time!
      mass_spectrum.setRtInSeconds(tims_frame.get()->getTime());

      // We do want to document the ms level of the spectrum and possibly the
      // precursor's m/z and charge.
      unsigned int frame_ms_level = tims_frame.get()->getMsLevel();
      mass_spectrum.setMsLevel(frame_ms_level);

      if(frame_ms_level > 1)
        {
          // FIXME
          // We want to document the precusor's m/z, z and index.
        }

      mass_spectrum.setDtInMilliSeconds(-1);

      mass_spectrum.setEmptyMassSpectrum(false);

      mass_spectrum.setMassSpectrumSPtr(
        std::make_shared<MassSpectrum>(spectrum));

      handler.setQualifiedMassSpectrum(mass_spectrum);

      scan_index += scan_count;
    }
}


void
TimsFramesMsRunReader::readSpectrumCollectionByMsLevel(
  [[maybe_unused]] SpectrumCollectionHandlerInterface &handler,
  [[maybe_unused]] unsigned int ms_level)
{

  qDebug();
}


std::size_t
TimsFramesMsRunReader::spectrumListSize() const
{
  return msp_timsData->getTotalNumberOfScans();
}


bool
TimsFramesMsRunReader::hasScanNumbers() const
{
  return false;
}


bool
TimsFramesMsRunReader::releaseDevice()
{
  msp_timsData = nullptr;
  return true;
}

bool
TimsFramesMsRunReader::acquireDevice()
{
  if(msp_timsData == nullptr)
    {
      initialize();
    }
  return true;
}


XicCoordSPtr
TimsFramesMsRunReader::newXicCoordSPtrFromSpectrumIndex(
  std::size_t spectrum_index [[maybe_unused]],
  pappso::PrecisionPtr precision [[maybe_unused]]) const
{
  throw ExceptionNotImplemented(QObject::tr("Not implemented %1 %2 %3")
                                  .arg(__FILE__)
                                  .arg(__FUNCTION__)
                                  .arg(__LINE__));
}

XicCoordSPtr
TimsFramesMsRunReader::newXicCoordSPtrFromQualifiedMassSpectrum(
  const pappso::QualifiedMassSpectrum &mass_spectrum [[maybe_unused]],
  pappso::PrecisionPtr precision [[maybe_unused]]) const
{
  throw ExceptionNotImplemented(QObject::tr("Not implemented %1 %2 %3")
                                  .arg(__FILE__)
                                  .arg(__FUNCTION__)
                                  .arg(__LINE__));
}

TimsDataSp
TimsFramesMsRunReader::getTimsDataSPtr()
{
  acquireDevice();
  return msp_timsData;
}


Trace
TimsFramesMsRunReader::getTicChromatogram()
{
  // Use the Sqlite database to fetch the total ion current chromatogram (TIC
  // chromatogram).

  acquireDevice();

  return msp_timsData->getTicChromatogram();
}


Trace
TimsFramesMsRunReader::computeTicChromatogram()
{

  // We want to compute the TIC chromatogram, not load the chromatogram that is
  // located in the SQL database.
  //
  // For this, we need to iterated into the frames and ask for MS1 spectra only.
  // msp_timsData has that information:
  //
  // std::vector<FrameIdDescr> m_frameIdDescrList;
  //
  // and

  // struct FrameIdDescr
  // {
  //   std::size_t m_frameId;   // frame id
  //   std::size_t m_size;      // frame size (number of TOF scans in frame)
  //   std::size_t m_cumulSize; // cumulative size
  // };

  Trace tic_chromatogram;

  const std::vector<FrameIdDescr> frame_descr_list =
    msp_timsData->getFrameIdDescrList();

  for(FrameIdDescr frame_id_descr : frame_descr_list)
    {
      TimsFrameCstSPtr tims_frame_csp =
        msp_timsData->getTimsFrameCstSPtrCached(frame_id_descr.m_frameId);
      std::size_t scan_begin = 0;
      std::size_t scan_end   = tims_frame_csp->getTotalNumberOfScans() - 1;

      // By convention, a TIC chromatogram is only performed using MS1 spectra.
      if(tims_frame_csp->getMsLevel() == 1)
        {

          double rt = tims_frame_csp->getTime();

          tic_chromatogram.append(DataPoint(
            rt,
            tims_frame_csp->cumulateScansIntensities(scan_begin, scan_end)));
        }
      else
        continue;
    }

  return tic_chromatogram;
}

