/**
 * \file pappsomspp/masschroq/msrunpeptidelist.cpp
 * \date 25/09/2024
 * \author Olivier Langella
 * \brief msrun model including fragmented peptides in masschroqlite
 */

/*******************************************************************************
 * Copyright (c) 2024 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of MassChroQ.
 *
 *     MassChroQ 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.
 *
 *     MassChroQ 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 MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "msrunpeptidelist.h"
#include "../xicextractor/msrunxicextractorfactory.h"
#include "pappsomspp/core/pappsoexception.h"
#include <QtConcurrent>

pappso::masschroq::MsRunPeptideList::MsRunPeptideList(pappso::masschroq::MsRunSp msrun)
{
  msp_msRun = msrun;
}

pappso::masschroq::MsRunPeptideList::MsRunPeptideList(
  const pappso::masschroq::MsRunPeptideList &other)
{
  msp_msRun = other.msp_msRun;
}

pappso::masschroq::MsRunPeptideList::~MsRunPeptideList()
{
}

void
pappso::masschroq::MsRunPeptideList::addPeptideSpectrumIndexObservation(
  pappso::masschroq::PeptideSp peptide_sp,
  pappso::masschroq::PeptideLabel *p_label,
  std::size_t spectrum_index,
  std::uint8_t charge)
{

  auto it = m_peptideObservationList.insert(
    {peptide_sp.get(), std::make_shared<pappso::masschroq::PeptideObservation>(peptide_sp)});
  pappso::masschroq::PrecursorSp precursor_sp =
    msp_msRun.get()->getPrecursorSPtrBySpectrumIndex(spectrum_index);
  it.first->second.get()->addObservation({spectrum_index, charge, precursor_sp, true, p_label});

  peptide_sp.get()->addMsRunXicCoordCharge({msp_msRun.get(),
                                            precursor_sp.get()->getXicCoordSPtr(),
                                            charge,
                                            precursor_sp.get()->getIntensity()});
  qDebug() << "m_peptideObservationList.size()=" << m_peptideObservationList.size();
}

void
pappso::masschroq::MsRunPeptideList::addPeptideScanNumberObservation(
  pappso::masschroq::PeptideSp peptide_sp,
  pappso::masschroq::PeptideLabel *p_label,
  std::size_t scan_number,
  std::uint8_t charge)
{

  auto it = m_peptideObservationList.insert(
    {peptide_sp.get(), std::make_shared<pappso::masschroq::PeptideObservation>(peptide_sp)});
  pappso::masschroq::PrecursorSp precursor_sp =
    msp_msRun.get()->getPrecursorSPtrByScanNumber(scan_number);
  it.first->second.get()->addObservation({scan_number, charge, precursor_sp, false, p_label});

  peptide_sp.get()->addMsRunXicCoordCharge({msp_msRun.get(),
                                            precursor_sp.get()->getXicCoordSPtr(),
                                            charge,
                                            precursor_sp.get()->getIntensity()});
  qDebug() << "m_peptideObservationList.size()=" << m_peptideObservationList.size();
}

void
pappso::masschroq::MsRunPeptideList::quantify(const pappso::masschroq::MsRunGroup *msrun_group_p,
                                              const QString &tmp_dir,
                                              pappso::UiMonitorInterface &ui_monitor,
                                              const QuantificationMethodSp &quantification_method)
{
  m_peptideMeasurementsList.clear();
  ui_monitor.appendText(
    QString("Starting quantification for msrun %1")
      .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId().get()->getFileName()));

  qDebug() << m_peptideObservationList.size();
  pappso::MsRunXicExtractorFactory::getInstance().setTmpDir(tmp_dir);

  qDebug();
  pappso::MsRunXicExtractorFactory::getInstance().setMsRunXicExtractorFactoryType(
    pappso::MsRunXicExtractorFactoryType::diskbuffer);

  qDebug();
  ui_monitor.appendText(QString("Preparing extraction list"));
  std::vector<pappso::XicCoordSPtr> xic_coord_list =
    buildXicCoordList(msrun_group_p, quantification_method);

  ui_monitor.appendText(QString("Preparing extraction list done"));

  ui_monitor.appendText(QString("Building XIC extractor"));
  pappso::MsRunXicExtractorInterfaceSp extractor_pwiz =
    pappso::MsRunXicExtractorFactory::getInstance().buildMsRunXicExtractorSp(
      msp_msRun.get()->getMsRunReaderSPtr());

  qDebug();
  extractor_pwiz->setXicExtractMethod(quantification_method.get()->getXicExtractMethod());

  // qWarning() << quantification_method.get()->getXicExtractionRtRange();

  extractor_pwiz.get()->setRetentionTimeAroundTarget(
    quantification_method.get()->getXicExtractionRtRange());

  qDebug();
  pappso::FilterInterfaceCstSPtr csp_filter_suite = quantification_method.get()->getXicFilter();

  qDebug();
  extractor_pwiz.get()->setPostExtractionTraceFilterCstSPtr(csp_filter_suite);

  qDebug();

  ui_monitor.appendText(QString("Building XIC extractor done"));
  ui_monitor.appendText(QString("Extracting"));
  extractor_pwiz.get()->extractXicCoordSPtrListParallelized(ui_monitor, xic_coord_list);

  // qWarning() << "size of first xic=" << xic_coord_list.at(0).get()->xicSptr.get()->size();
  qDebug();

  detectQuantifyPeaks(ui_monitor, quantification_method);


  ui_monitor.appendText(
    QString("Quantification for msrun %1 done")
      .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId().get()->getFileName()));
}


std::vector<pappso::XicCoordSPtr>
pappso::masschroq::MsRunPeptideList::buildXicCoordList(
  const pappso::masschroq::MsRunGroup *msrun_group_p,
  const QuantificationMethodSp &quantification_method)
{
  std::vector<pappso::XicCoordSPtr> xic_coord_list;

  for(auto pair_peptide : m_peptideObservationList)
    {
      pappso::masschroq::PeptideObservationSp peptide_events_sp = pair_peptide.second;

      addPeptideObservation2XicCoordList(
        msrun_group_p, quantification_method, pair_peptide.second, xic_coord_list);
      // peptide_events_sp.get()
    }


  return xic_coord_list;
}

void
pappso::masschroq::MsRunPeptideList::addPeptideObservation2XicCoordList(
  const pappso::masschroq::MsRunGroup *msrun_group_p,
  const QuantificationMethodSp &quantification_method,
  const pappso::masschroq::PeptideObservationSp &peptide_events_sp,
  std::vector<pappso::XicCoordSPtr> &xic_coord_list)
{
  PeptideMeasurementsSp peptide_measurements =
    std::make_shared<pappso::masschroq::PeptideMeasurements>(peptide_events_sp);

  m_peptideMeasurementsList.push_back(peptide_measurements);

  peptide_measurements.get()->prepareMeasurements(
    *(msp_msRun.get()->getMsRunReaderSPtr().get()->getMsRunId().get()),
    msrun_group_p,
    quantification_method);
  peptide_measurements.get()->pushBackXicCoordList(xic_coord_list);
}

void
pappso::masschroq::MsRunPeptideList::detectQuantifyPeaks(
  pappso::UiMonitorInterface &m_uiMonitor, const QuantificationMethodSp &quantification_method)
{

  m_uiMonitor.appendText(QString("Detect and quantify peak"));
  /*
    for(auto &measure : m_peptideMeasurementsList)
      {
        measure->detectQuantifyPeaks(quantification_method);
      }
  */

  std::function<void(const pappso::masschroq::PeptideMeasurementsSp &)> mapdetectQuantifyPeaks =
    [quantification_method](const pappso::masschroq::PeptideMeasurementsSp &measure) {
      measure->detectQuantifyPeaks(quantification_method);
    };


  QFuture<void> res =
    QtConcurrent::map<std::vector<pappso::masschroq::PeptideMeasurementsSp>::iterator>(
      m_peptideMeasurementsList.begin(), m_peptideMeasurementsList.end(), mapdetectQuantifyPeaks);
  res.waitForFinished();


  m_uiMonitor.appendText(QString("Detect and quantify peak done"));
}


const pappso::masschroq::MsRunSp &
pappso::masschroq::MsRunPeptideList::getMsRunSp() const
{
  return msp_msRun;
}

void
pappso::masschroq::MsRunPeptideList::flushChromatogramTraces()
{

  for(auto &measure : m_peptideMeasurementsList)
    {
      measure.get()->flushXics();
    }

  for(auto &mbr_measure : m_mbrPeptideMeasurementsList)
    {
      mbr_measure.get()->flushXics();
    }
}

std::shared_ptr<pappso::MsRunRetentionTime<QString>> &
pappso::masschroq::MsRunPeptideList::buildMsRunRetentionTimeSpOnPeptideObservations(
  const pappso::masschroq::AlignmentMethodSp &alignment_method)
{
  msp_msRunRetentionTime =
    std::make_shared<pappso::MsRunRetentionTime<QString>>(msp_msRun->getRetentionTimeLine());

  msp_msRunRetentionTime.get()->setMs2MedianFilter(
    pappso::FilterMorphoMedian(alignment_method.get()->getMs2TendencyWindow()));
  msp_msRunRetentionTime.get()->setMs2MeanFilter(
    pappso::FilterMorphoMean(alignment_method.get()->getMs2SmoothingWindow()));
  msp_msRunRetentionTime.get()->setMs1MeanFilter(
    pappso::FilterMorphoMean(alignment_method.get()->getMs1SmoothingWindow()));


  for(const auto &observation_pair : m_peptideObservationList)
    {
      QString peptide_id = observation_pair.first->getId();

      for(std::uint8_t charge : observation_pair.second->getObservedChargeStates())
        {

          msp_msRunRetentionTime.get()->addPeptideAsSeamark(
            QString("%1-%2").arg(peptide_id).arg(charge),
            observation_pair.second.get()->getBestXicCoordSPtrForCharge(charge).get()->rtTarget,
            1);
        }
    }
  /*
for(auto &one_xic_measure : measures.get()->getMeasurementList())
{
if((one_xic_measure.m_peakQualityCategory ==
PeakQualityCategory::aa) ||
(one_xic_measure.m_peakQualityCategory == PeakQualityCategory::a))
{
msp_msRunRetentionTime.get()->addPeptideAsSeamark(
peptide_id,
one_xic_measure.m_tracePeakSp.get()->getMaxXicElement().x,
one_xic_measure.m_tracePeakSp.get()->getMaxXicElement().y);
}
}
*/


  msp_msRunRetentionTime.get()->computeSeamarks();

  return msp_msRunRetentionTime;
}

void
pappso::masschroq::MsRunPeptideList::collectPeptidePeakRetentionTime(
  const pappso::MsRunRetentionTime<QString> *msrun_retention_time_reference_p) const
{
  for(const pappso::masschroq::PeptideMeasurementsSp &measures : m_peptideMeasurementsList)
    {
      try
        {
          pappso::masschroq::Peptide *peptide_p =
            measures.get()->getPeptideObservationSp().get()->getPeptideSp().get();
          peptide_p->setReferenceMsRunRetentionTimePtr(msrun_retention_time_reference_p);
          for(auto &one_xic_measure : measures.get()->getMeasurementList())
            {
              peptide_p->addAlignedPeakMeasurement(one_xic_measure,
                                                   *(msp_msRunRetentionTime.get()));
            }
        }
      catch(const pappso::PappsoException &error)
        {
          throw pappso::PappsoException(
            QObject::tr("error collecting peak retention time in msrun %1 :\n%2")
              .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId()->getXmlId())
              .arg(error.qwhat()));
        }
    }
}


void
pappso::masschroq::MsRunPeptideList::collectPeptideMs2RetentionTime(
  const pappso::MsRunRetentionTime<QString> *msrun_retention_time_reference_p) const
{
  // collect only peptides that were selected for measurement in the first pass
  for(const pappso::masschroq::PeptideMeasurementsSp &measures : m_peptideMeasurementsList)
    {
      try
        {

          pappso::masschroq::Peptide *peptide_p =
            measures.get()->getPeptideObservationSp().get()->getPeptideSp().get();
          peptide_p->setReferenceMsRunRetentionTimePtr(msrun_retention_time_reference_p);
          peptide_p->addAlignedPeptideObservation(
            *(measures.get()->getPeptideObservationSp().get()), *(msp_msRunRetentionTime.get()));
        }
      catch(const pappso::PappsoException &error)
        {
          throw pappso::PappsoException(
            QObject::tr("error collecting MS2 retention time in msrun %1 :\n%2")
              .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId()->getXmlId())
              .arg(error.qwhat()));
        }
    }
}


void
pappso::masschroq::MsRunPeptideList::quantifyMatchBetweenRun(
  const pappso::masschroq::MsRunGroup *msrun_group_p,
  const std::vector<pappso::masschroq::PeptideSp> &peptide_mbr_list,
  const QString &tmp_dir,
  pappso::UiMonitorInterface &ui_monitor,
  const QuantificationMethodSp &quantification_method)
{
  try
    {
      m_peptideMeasurementsList.clear();
      ui_monitor.appendText(
        QString("Starting quantification for msrun %1 (MBR)")
          .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId().get()->getFileName()));

      qDebug();
      pappso::MsRunXicExtractorFactory::getInstance().setTmpDir(tmp_dir);

      qDebug();
      pappso::MsRunXicExtractorFactory::getInstance().setMsRunXicExtractorFactoryType(
        pappso::MsRunXicExtractorFactoryType::diskbuffer);

      qDebug();
      ui_monitor.appendText(
        QString("MBR Preparing extraction list (%1)").arg(peptide_mbr_list.size()));


      std::vector<pappso::XicCoordSPtr> xic_coord_list;

      for(auto &peptide_sp : peptide_mbr_list)
        {
          /*
          pappso::masschroq::PeptideObservationSp peptide_events_sp = pair_peptide.second;

          addPeptideObservation2XicCoordList(quantification_method,
                                             pair_peptide.second,
                                             xic_coord_list,
                                             ni_minimum_abundance);
                                             */
          addMbrPeptideMeasurementsSp2XicCoordList(
            msrun_group_p, quantification_method, peptide_sp, xic_coord_list);
          // peptide_events_sp.get()
        }


      ui_monitor.appendText(QString("MBR Preparing extraction list done (%1 %2)")
                              .arg(m_mbrPeptideMeasurementsList.size())
                              .arg(xic_coord_list.size()));

      ui_monitor.appendText(QString("MBR Building XIC extractor"));
      pappso::MsRunXicExtractorInterfaceSp extractor_pwiz =
        pappso::MsRunXicExtractorFactory::getInstance().buildMsRunXicExtractorSp(
          msp_msRun.get()->getMsRunReaderSPtr());

      qDebug();
      extractor_pwiz->setXicExtractMethod(quantification_method.get()->getXicExtractMethod());

      qDebug();

      extractor_pwiz.get()->setRetentionTimeAroundTarget(
        quantification_method.get()->getXicExtractionRtRange());

      qDebug();
      pappso::FilterInterfaceCstSPtr csp_filter_suite = quantification_method.get()->getXicFilter();

      qDebug();
      extractor_pwiz.get()->setPostExtractionTraceFilterCstSPtr(csp_filter_suite);

      qDebug();

      ui_monitor.appendText(QString("MBR Building XIC extractor done"));
      ui_monitor.appendText(QString("MBR Extracting"));
      extractor_pwiz.get()->extractXicCoordSPtrListParallelized(ui_monitor, xic_coord_list);

      qDebug();

      mbrDetectQuantifyPeaks(ui_monitor, quantification_method);


      ui_monitor.appendText(
        QString("MBR Quantification for msrun %1 done")
          .arg(msp_msRun.get()->getMsRunReaderSPtr()->getMsRunId().get()->getFileName()));
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("error in %1 :\n%2").arg(__FUNCTION__).arg(error.qwhat()));
    }
}


void
pappso::masschroq::MsRunPeptideList::addMbrPeptideMeasurementsSp2XicCoordList(
  const pappso::masschroq::MsRunGroup *msrun_group_p,
  const pappso::masschroq::QuantificationMethodSp &quantification_method,
  const pappso::masschroq::PeptideSp &peptide_sp,
  std::vector<pappso::XicCoordSPtr> &xic_coord_list)
{
  MbrPeptideMeasurementsSp mbr_peptide_measurements =
    std::make_shared<pappso::masschroq::MbrPeptideMeasurements>(peptide_sp);

  m_mbrPeptideMeasurementsList.push_back(mbr_peptide_measurements);

  mbr_peptide_measurements.get()->prepareMeasurements(
    *(msp_msRun.get()->getMsRunReaderSPtr().get()->getMsRunId().get()),
    msrun_group_p,
    *(msp_msRunRetentionTime.get()),
    quantification_method);
  mbr_peptide_measurements.get()->pushBackXicCoordList(xic_coord_list);
}

void
pappso::masschroq::MsRunPeptideList::mbrDetectQuantifyPeaks(
  pappso::UiMonitorInterface &m_uiMonitor,
  const pappso::masschroq::QuantificationMethodSp &quantification_method)
{

  m_uiMonitor.appendText(QString("MBR Detect and quantify peak"));
  /*
    for(auto &measure : m_peptideMeasurementsList)
      {
        measure->detectQuantifyPeaks(quantification_method);
      }
  */

  std::function<void(const pappso::masschroq::MbrPeptideMeasurementsSp &)> mapdetectQuantifyPeaks =
    [quantification_method](const pappso::masschroq::MbrPeptideMeasurementsSp &measure) {
      measure->detectQuantifyPeaks(quantification_method);
    };


  QFuture<void> res =
    QtConcurrent::map<std::vector<pappso::masschroq::MbrPeptideMeasurementsSp>::iterator>(
      m_mbrPeptideMeasurementsList.begin(),
      m_mbrPeptideMeasurementsList.end(),
      mapdetectQuantifyPeaks);
  res.waitForFinished();


  m_uiMonitor.appendText(QString("Detect and quantify peak done"));
}


pappso::MsRunRetentionTime<QString> *
pappso::masschroq::MsRunPeptideList::getMsRunRetentionTimePtr() const
{
  return msp_msRunRetentionTime.get();
}
const pappso::MsRunRetentionTime<QString> *
pappso::masschroq::MsRunPeptideList::getMsRunRetentionTimeConstPtr() const
{
  return msp_msRunRetentionTime.get();
}

const std::vector<pappso::masschroq::PeptideMeasurementsSp> &
pappso::masschroq::MsRunPeptideList::getPeptideMeasurementsList() const
{
  return m_peptideMeasurementsList;
}

const std::vector<pappso::masschroq::MbrPeptideMeasurementsSp> &
pappso::masschroq::MsRunPeptideList::getMbrPeptideMeasurementsList() const
{
  return m_mbrPeptideMeasurementsList;
}

void
pappso::masschroq::MsRunPeptideList::clearMeasurements()
{

  for(auto &measure : m_peptideMeasurementsList)
    {
      measure.get()->flushXics();
    }

  for(auto &mbr_measure : m_mbrPeptideMeasurementsList)
    {
      mbr_measure.get()->flushXics();
    }
}
