/**
 * \file mcql/core/msrungroup.cpp
 * \date 10/10/2024
 * \author Olivier Langella
 * \brief group msrun together 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 "msrungroup.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"

pappso::masschroq::MsRunGroup::MsRunGroup(const QString &id,
                                          const std::vector<MsRunPeptideListSp> &msrun_list)
  : m_id(id), m_msRunPeptideListSpList(msrun_list)
{
  if(m_msRunPeptideListSpList.size() > 0)
    {
      m_hasTimsTofMobilityIndex =
        m_msRunPeptideListSpList.front().get()->getMsRunSp().get()->hasTimsTofMobilityIndex();
    }
}

pappso::masschroq::MsRunGroup::MsRunGroup(const pappso::masschroq::MsRunGroup &other)
  : m_id(other.m_id), m_msRunPeptideListSpList(other.m_msRunPeptideListSpList)
{
  m_hasTimsTofMobilityIndex = other.m_hasTimsTofMobilityIndex;
}

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

void
pappso::masschroq::MsRunGroup::alignRetentionTimeBetweenMsRuns(
  pappso::masschroq::CborOutputStream &cbor_output,
  [[maybe_unused]] pappso::UiMonitorInterface &m_uiMonitor) const
{
  if(msp_msRunPeptideListAlignmentReference.get() == nullptr)
    {
      throw pappso::PappsoException(QObject::tr("match between run requires an MSrun reference"));
    }
  cbor_output.getCborStreamWriter().append(QLatin1String("alignment"));
  cbor_output.getCborStreamWriter().startMap();
  cbor_output.getCborStreamWriter().append(QLatin1String("msrun_ref"));
  cbor_output.getCborStreamWriter().append(msp_msRunPeptideListAlignmentReference.get()
                                             ->getMsRunSp()
                                             .get()
                                             ->getMsRunReaderSPtr()
                                             .get()
                                             ->getMsRunId()
                                             .get()
                                             ->getXmlId());
  //   "alignment": {
  //       "msrun_ref": "msruna2",

  for(const pappso::masschroq::MsRunPeptideListSp &msrun_peptide_list : m_msRunPeptideListSpList)
    {
      msrun_peptide_list.get()->buildMsRunRetentionTimeSpOnPeptideObservations(msp_alignmentMethod);
    }
  const pappso::MsRunRetentionTime<QString> *reference_msrun_retetiontime_p =
    msp_msRunPeptideListAlignmentReference.get()->getMsRunRetentionTimeConstPtr();

  cbor_output.getCborStreamWriter().append(QLatin1String("corrections"));

  cbor_output.getCborStreamWriter().startMap();
  // "corrections": {
  for(const pappso::masschroq::MsRunPeptideListSp &msrun_peptide_list : m_msRunPeptideListSpList)
    {

      if(msrun_peptide_list.get() == msp_msRunPeptideListAlignmentReference.get())
        continue;


      /*
               "msruna1": {
                   "original": [1,2,3,4.5],
                   "aligned":[1,2,3,4.5]
               },
               "msruna2": {
                   "original": [1,2,3,4.5],
                   "aligned":[1,2,3,4.5]
               }*/
      msrun_peptide_list.get()->getMsRunRetentionTimePtr()->align(*reference_msrun_retetiontime_p);

      pappso::Trace report_common_MS2_delta_rt =
        msrun_peptide_list.get()->getMsRunRetentionTimePtr()->getCommonDeltaRt(
          reference_msrun_retetiontime_p->getSeamarks());

      cbor_output.getCborStreamWriter().append(msrun_peptide_list.get()
                                                 ->getMsRunSp()
                                                 .get()
                                                 ->getMsRunReaderSPtr()
                                                 .get()
                                                 ->getMsRunId()
                                                 .get()
                                                 ->getXmlId());
      cbor_output.writeMsRunRetentionTime(report_common_MS2_delta_rt,
                                          *msrun_peptide_list.get()->getMsRunRetentionTimePtr());
    }


  cbor_output.getCborStreamWriter().endMap();
  cbor_output.getCborStreamWriter().endMap();
}


void
pappso::masschroq::MsRunGroup::align(pappso::masschroq::CborOutputStream &cbor_output,
                                     const QString &align_id,
                                     pappso::UiMonitorInterface &m_uiMonitor) const
{

  cbor_output.getCborStreamWriter().startMap();
  cbor_output.getCborStreamWriter().append(QLatin1String("alignment_id"));
  cbor_output.getCborStreamWriter().append(align_id);
  cbor_output.getCborStreamWriter().append(QLatin1String("group_id"));
  cbor_output.getCborStreamWriter().append(getId());
  cbor_output.getCborStreamWriter().append(QLatin1String("timestamp1"));
  cbor_output.getCborStreamWriter().append(QDateTime::currentDateTime().toString(Qt::ISODate));

  // std::shared_ptr<pappso::MsRunRetentionTime<QString>>
  //  retention_time_reference_sp = nullptr;
  m_uiMonitor.appendText("Starting retention time alignment");
  alignRetentionTimeBetweenMsRuns(cbor_output, m_uiMonitor);
  m_uiMonitor.appendText("retention time alignment done");


  cbor_output.getCborStreamWriter().append(QLatin1String("timestamp2"));
  cbor_output.getCborStreamWriter().append(QDateTime::currentDateTime().toString(Qt::ISODate));
  cbor_output.getCborStreamWriter().endMap();
}


void
pappso::masschroq::MsRunGroup::quantify(
  pappso::masschroq::CborOutputStream &cbor_output,
  const QString &quantify_id,
  const QString &tmp_dir,
  pappso::UiMonitorInterface &m_uiMonitor,
  const pappso::masschroq::QuantificationMethodSp &quantification_method,
  const std::vector<pappso::masschroq::PeptideSp> &peptide_sp_list) const
{

  bool match_between_run = quantification_method.get()->getMatchBetweenRun();

  cbor_output.getCborStreamWriter().startMap();
  cbor_output.getCborStreamWriter().append("quantify_id");
  cbor_output.getCborStreamWriter().append(quantify_id);
  cbor_output.getCborStreamWriter().append("group_id");
  cbor_output.getCborStreamWriter().append(getId());
  cbor_output.getCborStreamWriter().append(QLatin1String("timestamp1"));
  cbor_output.getCborStreamWriter().append(QDateTime::currentDateTime().toString(Qt::ISODate));

  cbor_output.getCborStreamWriter().append("first_pass");
  cbor_output.getCborStreamWriter().startMap(m_msRunPeptideListSpList.size());


  const pappso::MsRunRetentionTime<QString> *msrun_rt_reference_p = nullptr;
  if(match_between_run)
    {

      if(msp_msRunPeptideListAlignmentReference.get() == nullptr)
        {
          throw pappso::PappsoException(
            QObject::tr("group %1 is not aligned, match between run is not possible").arg(getId()));
        }
      msrun_rt_reference_p =
        msp_msRunPeptideListAlignmentReference.get()->getMsRunRetentionTimeConstPtr();
    }


  for(const pappso::masschroq::MsRunPeptideListSp &msrun_peptide_list : m_msRunPeptideListSpList)
    {
      msrun_peptide_list.get()->quantify(this, tmp_dir, m_uiMonitor, quantification_method);


      if(match_between_run)
        {


          const pappso::MsRunRetentionTime<QString> *msrun_rt_p =
            msrun_peptide_list.get()->getMsRunRetentionTimeConstPtr();


          if((msrun_rt_reference_p == nullptr) || (msrun_rt_p == nullptr) ||
             ((!msrun_rt_p->isAligned()) && (msrun_rt_reference_p != msrun_rt_p)))
            {
              throw pappso::PappsoException(
                QObject::tr("msrun %1 is not aligned, match between run is not possible")
                  .arg(msrun_peptide_list.get()
                         ->getMsRunSp()
                         .get()
                         ->getMsRunReaderSPtr()
                         .get()
                         ->getMsRunId()
                         .get()
                         ->getXmlId()));
            }
          m_uiMonitor.appendText("Collecting peak apex");
          msrun_peptide_list.get()->collectPeptidePeakRetentionTime(msrun_rt_reference_p);
          msrun_peptide_list.get()->collectPeptideMs2RetentionTime(msrun_rt_reference_p);
        }

      cbor_output.writeQrDataBlock(m_uiMonitor, *msrun_peptide_list.get(), false);


      msrun_peptide_list.get()->clearMeasurements();
    }

  cbor_output.getCborStreamWriter().endMap();

  cbor_output.getCborStreamWriter().append(QLatin1String("timestamp2"));
  cbor_output.getCborStreamWriter().append(QDateTime::currentDateTime().toString(Qt::ISODate));
  if(match_between_run)
    {
      if(msrun_rt_reference_p != nullptr)
        { // second pass

          m_uiMonitor.appendText(
            QObject::tr("Computing consensus retention time (%1)").arg(peptide_sp_list.size()));
          for(const pappso::masschroq::PeptideSp &peptide_sp : peptide_sp_list)
            {
              peptide_sp.get()->computeConsensusRetentionTime();
            }

          cbor_output.getCborStreamWriter().append("second_pass");
          cbor_output.getCborStreamWriter().startMap(m_msRunPeptideListSpList.size());
          for(const pappso::masschroq::MsRunPeptideListSp &msrun_peptide_list :
              m_msRunPeptideListSpList)
            {
              const pappso::masschroq::MsRun *msrun_p =
                msrun_peptide_list.get()->getMsRunSp().get();
              std::vector<pappso::masschroq::PeptideSp> peptide_mbr_list;
              for(const pappso::masschroq::PeptideSp &peptide_sp : peptide_sp_list)
                {
                  if(!peptide_sp.get()->isObservedInMsRunSp(msrun_p))
                    {
                      peptide_mbr_list.push_back(peptide_sp);
                    }
                }
              msrun_peptide_list.get()->quantifyMatchBetweenRun(
                this, peptide_mbr_list, tmp_dir, m_uiMonitor, quantification_method);


              cbor_output.writeQrDataBlock(m_uiMonitor, *msrun_peptide_list.get(), true);

              msrun_peptide_list.get()->clearMeasurements();
            }

          cbor_output.getCborStreamWriter().endMap();
        }
    }

  cbor_output.getCborStreamWriter().append(QLatin1String("timestamp3"));
  cbor_output.getCborStreamWriter().append(QDateTime::currentDateTime().toString(Qt::ISODate));
  cbor_output.getCborStreamWriter().endMap();
}

void
pappso::masschroq::MsRunGroup::setAlignmentMethodSp(
  const pappso::masschroq::AlignmentMethodSp &alignment_method_sp, const QString &msrun_ref_id)
{
  auto it = find_if(m_msRunPeptideListSpList.begin(),
                    m_msRunPeptideListSpList.end(),
                    [msrun_ref_id](const pappso::masschroq::MsRunPeptideListSp &msrun) {
                      if(msrun.get()
                           ->getMsRunSp()
                           .get()
                           ->getMsRunReaderSPtr()
                           .get()
                           ->getMsRunId()
                           .get()
                           ->getXmlId() == msrun_ref_id)
                        return true;
                      return false;
                    });
  if(it != m_msRunPeptideListSpList.end())
    {
      msp_alignmentMethod                    = alignment_method_sp;
      msp_msRunPeptideListAlignmentReference = *it;
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("msrun_id %1 not found in group %2").arg(msrun_ref_id).arg(m_id));
    }
}

bool
pappso::masschroq::MsRunGroup::contains(const pappso::masschroq::MsRun *msrun_p) const
{
  auto it = find_if(m_msRunPeptideListSpList.begin(),
                    m_msRunPeptideListSpList.end(),
                    [msrun_p](const pappso::masschroq::MsRunPeptideListSp &msrun) {
                      return (msrun.get()->getMsRunSp().get() == msrun_p);
                    });
  return (it != m_msRunPeptideListSpList.end());
}

void
pappso::masschroq::MsRunGroup::setIonMobilityGridSp(
  const std::shared_ptr<pappso::IonMobilityGrid> &ion_mobility_grid_sp)
{
  msp_ionMobilityGrid = ion_mobility_grid_sp;
}

bool
pappso::masschroq::MsRunGroup::hasTimsTofMobilityIndex() const
{
  return m_hasTimsTofMobilityIndex;
}

const std::shared_ptr<pappso::IonMobilityGrid> &
pappso::masschroq::MsRunGroup::getIonMobilityGridSp() const
{
  return msp_ionMobilityGrid;
}

const QString &
pappso::masschroq::MsRunGroup::getId() const
{
  return m_id;
}
