/**
 * \file pappsomspp/msrun/private/timsmsrunreaderms2.cpp
 * \date 10/09/2019
 * \author Olivier Langella
 * \brief MSrun file reader for native Bruker TimsTOF specialized for MS2
 * purpose
 */


/*******************************************************************************
 * 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 "pappsomspp/core/msrun/private/timsmsrunreaderms2selected.h"
#include "pappsomspp/core/exception/exceptionnotimplemented.h"
#include "../../vendors/tims/timsddaprecursors.h"
#include <QDebug>
#include <QtConcurrent>

using namespace pappso;

TimsMsRunReaderMs2Selected::TimsMsRunReaderMs2Selected(MsRunIdCstSPtr &msrun_id_csp)
  : TimsMsRunReaderMs2(msrun_id_csp)
{
  initialize();
}

TimsMsRunReaderMs2Selected::~TimsMsRunReaderMs2Selected()
{
  if(mpa_timsData != nullptr)
    {
      delete mpa_timsData;
    }
}

void
pappso::TimsMsRunReaderMs2Selected::initialize()
{
  mpa_timsData = new TimsData(mcsp_msRunId.get()->getFileName());

  mp_timsDdaPrecursors = mpa_timsData->getTimsDdaPrecursorsPtr();
}

void
TimsMsRunReaderMs2Selected::setMs2FilterCstSPtr(pappso::FilterInterfaceCstSPtr filter)
{
  if(mp_timsDdaPrecursors != nullptr)
    {
      mp_timsDdaPrecursors->setMs2FilterCstSPtr(filter);
    }
  else
    {
      throw PappsoException(
        QObject::tr("ERROR in TimsMsRunReaderMs2Selected::setMs2FilterCstSPtr "
                    "mp_timsDdaPrecursors is null"));
    }
}

void
TimsMsRunReaderMs2Selected::setMs1FilterCstSPtr(pappso::FilterInterfaceCstSPtr filter)
{
  if(mp_timsDdaPrecursors != nullptr)
    {
      mp_timsDdaPrecursors->setMs1FilterCstSPtr(filter);
    }
  else
    {
      throw PappsoException(
        QObject::tr("ERROR in TimsMsRunReaderMs2Selected::setMs1FilterCstSPtr "
                    "mp_timsDdaPrecursors is null"));
    }
}

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


pappso::MassSpectrumSPtr
TimsMsRunReaderMs2Selected::massSpectrumSPtr(std::size_t spectrum_index)
{
  QualifiedMassSpectrum mass_spectrum = qualifiedMassSpectrum(spectrum_index, true);
  return mass_spectrum.getMassSpectrumSPtr();
}


pappso::MassSpectrumCstSPtr
TimsMsRunReaderMs2Selected::massSpectrumCstSPtr(std::size_t spectrum_index)
{
  QualifiedMassSpectrum mass_spectrum = qualifiedMassSpectrum(spectrum_index, true);
  return mass_spectrum.getMassSpectrumSPtr();
}


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

  std::size_t precursor_index = (spectrum_index / 2) + 1;

  TimsDdaPrecursors::SpectrumDescr spectrum_descr =
    mp_timsDdaPrecursors->getSpectrumDescrWithPrecursorId(precursor_index);

  if(spectrum_index % 2 == 0)
    {
      qDebug();
      // this is an MS1 spectrum
      QualifiedMassSpectrum mass_spectrum_ms1;
      mp_timsDdaPrecursors->getQualifiedMs1MassSpectrumBySpectrumDescr(
        getMsRunId(), mass_spectrum_ms1, spectrum_descr, want_binary_data);
      qDebug(); // << mass_spectrum_ms1.toString();

      // qDebug() << mass_spectrum_ms1.getMassSpectrumSPtr().get()->toString();
      return mass_spectrum_ms1;
    }
  else
    {
      qDebug();
      QualifiedMassSpectrum mass_spectrum_ms2;
      if(spectrum_descr.ms2_index != spectrum_index)
        {
          qDebug();
          throw PappsoException(
            QObject::tr("ERROR in %1 %2 %3 spectrum_descr.ms2_index != spectrum_index")
              .arg(__FILE__)
              .arg(__FUNCTION__)
              .arg(__LINE__));
        }
      mp_timsDdaPrecursors->getQualifiedMs2MassSpectrumBySpectrumDescr(
        getMsRunId(), mass_spectrum_ms2, spectrum_descr, want_binary_data);
      qDebug(); // << mass_spectrum_ms2.toString();

      // qDebug() << mass_spectrum_ms2.getMassSpectrumSPtr().get()->toString();
      return mass_spectrum_ms2;
    }
}


void
TimsMsRunReaderMs2Selected::readSpectrumCollection(SpectrumCollectionHandlerInterface &handler)
{
  const bool want_binary_data = handler.needPeakList();
  // const bool want_binary_data = false;

  // We'll need it to perform the looping in the spectrum list.
  std::size_t spectrum_list_size = spectrumListSize();

  //   qDebug() << "The spectrum list has size:" << spectrum_list_size;

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

  std::vector<size_t> selected_precursor{63905,
                                         73549,
                                         105675,
                                         130439,
                                         177297,
                                         177369,
                                         177483,
                                         190453,
                                         196967,
                                         246691,
                                         271215,
                                         289423,
                                         310669};
  // QFile temp_output("/data/temp.txt");
  // temp_output.open(QIODevice::WriteOnly | QIODevice::Text);
  // QTextStream out(&temp_output);

  // Iterate in the full list of spectra.
  bool readAhead = handler.isReadAhead();

  if(readAhead)
    {

      std::size_t process_list_size = 300;

      struct tmp_item
      {
        QualifiedMassSpectrum qualified_mass_spectrum;
        std::size_t iter;
        bool want_binary_data;
      };

      for(std::size_t i = 0; i < spectrum_list_size; i += process_list_size)
        {
          // QTextStream out(&temp_output);
          qDebug();
          // If the user of this reader instance wants to stop reading the
          // spectra, then break this loop.
          if(handler.shouldStop())
            {
              qDebug() << "The operation was cancelled. Breaking the loop.";
              break;
            }
          std::vector<tmp_item> item_list;
          for(std::size_t iter = 0; (iter < process_list_size) && ((iter + i) < spectrum_list_size);
              iter++)
            {
              if(std::find(selected_precursor.begin(), selected_precursor.end(), iter + i) !=
                 selected_precursor.end())
                {
                  bool get_data = want_binary_data;
                  if((iter + i) % 2 == 0)
                    { // MS1
                      get_data = handler.needMsLevelPeakList(1);
                    }
                  else
                    {
                      get_data = handler.needMsLevelPeakList(2);
                    }

                  item_list.push_back({QualifiedMassSpectrum(), iter + i, get_data});
                }
            }
          qDebug() << item_list.size();
          // Use QtConcurrentBlocking::mapped to apply the scale function to all
          // the images in the list.
          QtConcurrent::blockingMap(item_list.begin(), item_list.end(), [this](tmp_item &one_item) {
            qDebug() << one_item.iter;
            one_item.qualified_mass_spectrum =
              qualifiedMassSpectrum(one_item.iter, one_item.want_binary_data);

            // qDebug() << one_item.qualified_mass_spectrum.size() << " " <<
            // one_item.qualified_mass_spectrum.getMassSpectrumSPtr().get()->toString();
          });

          qDebug() << item_list.size();
          for(auto &item : item_list)
            {
              // qDebug() <<
              // item.qualified_mass_spectrum.getMassSpectrumSPtr()
              //              .get()
              //              ->toString();
              handler.setQualifiedMassSpectrum(item.qualified_mass_spectrum);
              qDebug();
            }
        }
    }
  else
    {
      for(std::size_t iter = 0; iter < spectrum_list_size; iter++)
        {
          qDebug();
          // If the user of this reader instance wants to stop reading the
          // spectra, then break this loop.
          if(handler.shouldStop())
            {
              qDebug() << "The operation was cancelled. Breaking the loop.";
              break;
            }
          bool get_data = want_binary_data;
          if(iter % 2 == 0)
            { // MS1
              if(!handler.needMsLevelPeakList(1))
                {
                  get_data = false;
                }
            }
          QualifiedMassSpectrum qualified_mass_spectrum = qualifiedMassSpectrum(iter, get_data);
          handler.setQualifiedMassSpectrum(qualified_mass_spectrum);
          qDebug();
        }
    }
  // End of
  // for(std::size_t iter = 0; iter < spectrum_list_size; iter++)

  // Now let the loading handler know that the loading of the data has
  // ended. The handler might need this "signal" to perform additional tasks
  // or to cleanup cruft.

  // qDebug() << "Loading ended";
  // temp_output.close();
  handler.loadingEnded();
}

void
TimsMsRunReaderMs2Selected::readSpectrumCollection2([[maybe_unused]] const MsRunReadConfig &config,
                                                    SpectrumCollectionHandlerInterface &handler)
{
  return readSpectrumCollection(handler);
}

std::size_t
TimsMsRunReaderMs2Selected::spectrumListSize() const
{
  return (mp_timsDdaPrecursors->getTotalPrecursorCount() * 2);
}


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


std::size_t
TimsMsRunReaderMs2Selected::spectrumStringIdentifier2SpectrumIndex(
  const QString &spectrum_identifier [[maybe_unused]])
{
  throw pappso::ExceptionNotImplemented(
    QObject::tr("%1 %2 %3 not implemented").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__));
}
