
/**
 * \file pappsomspp/masschroq/output/cboroutputstream.cpp
 * \date 02/01/2025
 * \author Olivier Langella
 * \brief quantification result cbor writer for MassChroqLight
 */

/*******************************************************************************
 * Copyright (c) 2025 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 "cboroutputstream.h"
#include <QDateTime>
#include <QCborMap>
#include <QThreadPool>
#include "pappsomspp/core/masschroq/peptide.h"
#include "pappsomspp/core/masschroq/peptideobservation.h"
#include "pappsomspp/core/utils.h"
#include "pappsomspp/core/masschroq/utils.h"
#include "pappsomspp/core/processing/filters/filterresample.h"

namespace pappso
{
masschroq::CborOutputStream::CborOutputStream(QIODevice *device)
{
  mpa_writer = new cbor::CborStreamWriter(device);
  mpa_writer->startMap();
}

masschroq::CborOutputStream::~CborOutputStream()
{
}

void
masschroq::CborOutputStream::setIsPeakShapeOutput(bool is_shape_output, double margin_in_seconds)
{
  m_isPeakShapeOutput        = is_shape_output;
  m_peakShapeMarginInSeconds = margin_in_seconds;
}

void
masschroq::CborOutputStream::setIsTraceOutput(bool is_trace_output)
{
  m_isTraceOutput = is_trace_output;
}

void
masschroq::CborOutputStream::writeActionBegin(const QString &operation)
{
  mpa_writer->writeInformations(
    "masschroq", masschroq::Utils::getVersion(), "quantification", operation);
}


void
masschroq::CborOutputStream::writeActionEnd()
{
  mpa_writer->append(QLatin1String("end"));
  mpa_writer->startMap();
  mpa_writer->append(QLatin1String("timestamp"));
  mpa_writer->append(QDateTime::currentDateTime().toString(Qt::ISODate));
  mpa_writer->endMap();
}
void
masschroq::CborOutputStream::close()
{
  writeActionEnd();
  mpa_writer->endMap();

  delete mpa_writer;
}

void
masschroq::CborOutputStream::writeJsonObject(const QString &name, const QJsonObject &json_object)
{
  mpa_writer->append(name);
  QCborMap map;
  map = map.fromJsonObject(json_object);
  map.toCborValue().toCbor(*mpa_writer);
}

QCborStreamWriter &
masschroq::CborOutputStream::getCborStreamWriter()
{
  return (*mpa_writer);
}


void
masschroq::CborOutputStream::writeMsRun(const masschroq::MsRun &msrun)
{
  mpa_writer->append(QLatin1String("msrun"));
  mpa_writer->startMap(3);
  mpa_writer->append(QLatin1String("id"));
  mpa_writer->append(msrun.getMsRunReaderSPtr().get()->getMsRunId().get()->getXmlId());
  mpa_writer->append(QLatin1String("filename"));
  mpa_writer->append(msrun.getMsRunReaderSPtr().get()->getMsRunId().get()->getFileName());
  mpa_writer->append(QLatin1String("sample"));
  mpa_writer->append(msrun.getMsRunReaderSPtr().get()->getMsRunId().get()->getSampleName());
  mpa_writer->endMap();
}


void
masschroq::CborOutputStream::writePeptideMeasurements(
  const masschroq::PeptideMeasurements &peptide_measurements,
  const MsRunRetentionTime<QString> *msrun_retention_time_p)
{
  masschroq::PeptideSp peptide_sp =
    peptide_measurements.getPeptideObservationSp().get()->getPeptideSp();

  mpa_writer->append(peptide_sp.get()->getId());
  mpa_writer->startMap();
  // mpa_writer->append(QLatin1String("peptide_id"));
  // mpa_writer->append(peptide_sp.get()->getId());
  mpa_writer->append(QLatin1String("proforma"));
  mpa_writer->append(peptide_sp.get()->getPappsoPeptideSp().get()->toProForma());


  mpa_writer->append(QLatin1String("mods"));
  mpa_writer->append(peptide_sp.get()->getMods());

  std::size_t size_measurement = peptide_measurements.getMeasurementList().size();
  if(size_measurement > 0)
    {
      mpa_writer->append(QLatin1String("rt_target"));
      mpa_writer->append(
        peptide_measurements.getMeasurementList().at(0).msp_xicCoord.get()->rtTarget);
    }

  mpa_writer->append(QLatin1String("xics"));
  mpa_writer->startArray(size_measurement);
  for(const masschroq::PeptideMeasurements::Measurement &measure :
      peptide_measurements.getMeasurementList())
    {
      // write(peptide_sp, measure, msrun_retention_time_p);
      writeMeasurement(measure, msrun_retention_time_p);
    }
  mpa_writer->endArray();

  mpa_writer->endMap();
}

void
masschroq::CborOutputStream::writeMeasurement(
  const masschroq::PeptideMeasurements::Measurement &measurement,
  const MsRunRetentionTime<QString> *msrun_retention_time_p)
{

  mpa_writer->startMap();
  mpa_writer->append(QLatin1String("charge"));
  mpa_writer->append(measurement.m_charge);

  mpa_writer->append(QLatin1String("mz"));
  mpa_writer->append(measurement.msp_xicCoord.get()->mzRange.getMz());

  if(m_isXicCoordOutput)
    {
      measurement.msp_xicCoord.get()->writeCborStream(*mpa_writer);
    }

  if(measurement.m_peptideNaturalIsotopeAverageSp.get() != nullptr)
    {
      mpa_writer->append(QLatin1String("isotope"));
      mpa_writer->append(measurement.m_peptideNaturalIsotopeAverageSp.get()->getIsotopeNumber());

      mpa_writer->append(QLatin1String("rank"));
      mpa_writer->append(measurement.m_peptideNaturalIsotopeAverageSp.get()->getIsotopeRank());

      mpa_writer->append(QLatin1String("th_ratio"));
      mpa_writer->append(measurement.m_peptideNaturalIsotopeAverageSp.get()->getIntensityRatio());
    }


  mpa_writer->append(QLatin1String("quality"));
  mpa_writer->append(masschroq::Utils::enumToString(measurement.m_peakQualityCategory));

  // label
  if(measurement.mp_peptideLabel != nullptr)
    {
      mpa_writer->append(QLatin1String("label"));
      mpa_writer->append(measurement.mp_peptideLabel->getLabel());
    }

  if(m_isTraceOutput && (measurement.msp_xicCoord.get()->xicSptr.get() != nullptr))
    {
      mpa_writer->append(QLatin1String("trace"));
      writeTrace(*(measurement.msp_xicCoord.get()->xicSptr.get()));
    }


  if(measurement.m_tracePeakSp.get() != nullptr)
    {

      if(m_isPeakShapeOutput && (measurement.msp_xicCoord.get()->xicSptr.get() != nullptr))
        {

          mpa_writer->append(QLatin1String("peak_shape"));
          mpa_writer->startMap();
          mpa_writer->append(QLatin1String("trace"));

          TraceSPtr trace_sp;

          FilterResampleKeepXRange cut_xic(
            measurement.m_tracePeakSp.get()->getLeftBoundary().x - m_peakShapeMarginInSeconds,
            measurement.m_tracePeakSp.get()->getRightBoundary().x + m_peakShapeMarginInSeconds);

          trace_sp = measurement.msp_xicCoord.get()->xicSptr;
          trace_sp.get()->filter(cut_xic);


          writeTrace(*(trace_sp.get()));
          mpa_writer->endMap();
        }

      mpa_writer->append(QLatin1String("peak"));
      writeTracePeak(*(measurement.m_tracePeakSp.get()), msrun_retention_time_p);
    }
  mpa_writer->endMap();
}


void
masschroq::CborOutputStream::writeTrace(const Trace &trace)
{
  mpa_writer->startMap(2);
  mpa_writer->append(QLatin1String("x"));
  mpa_writer->startArray(trace.size());
  for(double x : trace.xValues())
    {
      mpa_writer->append(x);
    }
  mpa_writer->endArray();


  mpa_writer->append(QLatin1String("y"));
  mpa_writer->startArray(trace.size());
  for(double y : trace.yValues())
    {
      mpa_writer->append(y);
    }
  mpa_writer->endArray();
  mpa_writer->endMap();
}

void
masschroq::CborOutputStream::writeTracePeak(
  const TracePeak &peak, const MsRunRetentionTime<QString> *msrun_retention_time_p)
{
  mpa_writer->startMap();
  mpa_writer->append(QLatin1String("area"));
  mpa_writer->append(peak.getArea());

  mpa_writer->append(QLatin1String("max_intensity"));
  mpa_writer->append(peak.getMaxXicElement().y);

  mpa_writer->append(QLatin1String("rt"));
  mpa_writer->startArray();
  mpa_writer->append(peak.getLeftBoundary().x);
  mpa_writer->append(peak.getMaxXicElement().x);
  mpa_writer->append(peak.getRightBoundary().x);
  mpa_writer->endArray();

  if((msrun_retention_time_p != nullptr) && msrun_retention_time_p->isAligned())
    {
      mpa_writer->append(QLatin1String("aligned_rt"));
      mpa_writer->startArray();
      mpa_writer->append(
        msrun_retention_time_p->translateOriginal2AlignedRetentionTime(peak.getLeftBoundary().x));
      mpa_writer->append(
        msrun_retention_time_p->translateOriginal2AlignedRetentionTime(peak.getMaxXicElement().x));
      mpa_writer->append(
        msrun_retention_time_p->translateOriginal2AlignedRetentionTime(peak.getRightBoundary().x));
      mpa_writer->endArray();
    }

  mpa_writer->endMap();
}


void
masschroq::CborOutputStream::writeMbrPeptideMeasurements(
  const masschroq::MbrPeptideMeasurements &mbr_peptide_measurements,
  const MsRunRetentionTime<QString> *msrun_retention_time_p)
{
  masschroq::PeptideSp peptide_sp = mbr_peptide_measurements.getPeptideSp();

  mpa_writer->append(peptide_sp.get()->getId());
  mpa_writer->startMap();
  mpa_writer->append(QLatin1String("proforma"));
  mpa_writer->append(peptide_sp.get()->getPappsoPeptideSp().get()->toProForma());


  mpa_writer->append(QLatin1String("mods"));
  mpa_writer->append(peptide_sp.get()->getMods());


  std::size_t size_measurement = mbr_peptide_measurements.getMeasurementList().size();
  if(size_measurement > 0)
    {
      mpa_writer->append(QLatin1String("rt_target"));
      mpa_writer->append(
        mbr_peptide_measurements.getMeasurementList().at(0).msp_xicCoord.get()->rtTarget);
    }

  mpa_writer->append(QLatin1String("xics"));
  mpa_writer->startArray(mbr_peptide_measurements.getMeasurementList().size());
  for(const masschroq::PeptideMeasurements::Measurement &measure :
      mbr_peptide_measurements.getMeasurementList())
    {
      // write(peptide_sp, measure, msrun_retention_time_p);
      writeMeasurement(measure, msrun_retention_time_p);
    }
  mpa_writer->endArray();

  mpa_writer->endMap();
}

void
masschroq::CborOutputStream::writeQrDataBlock(UiMonitorInterface &monitor,
                                              const masschroq::MsRunPeptideList &msrun_peptide_list,
                                              bool is_mbr)
{

  quint64 size;
  if(is_mbr)
    {
      size = msrun_peptide_list.getMbrPeptideMeasurementsList().size();

      monitor.setStatus(QString("Writing MBR quantification results for msrun %1")
                          .arg(msrun_peptide_list.getMsRunSp()
                                 .get()
                                 ->getMsRunReaderSPtr()
                                 .get()
                                 ->getMsRunId()
                                 .get()
                                 ->getSampleName()));
    }
  else
    {
      size = msrun_peptide_list.getPeptideMeasurementsList().size();

      monitor.setStatus(QString("Writing quantification results for msrun %1")
                          .arg(msrun_peptide_list.getMsRunSp()
                                 .get()
                                 ->getMsRunReaderSPtr()
                                 .get()
                                 ->getMsRunId()
                                 .get()
                                 ->getSampleName()));
    }
  monitor.setTotalSteps(size);
  mpa_writer->append(msrun_peptide_list.getMsRunSp()
                       .get()
                       ->getMsRunReaderSPtr()
                       .get()
                       ->getMsRunId()
                       .get()
                       ->getXmlId());
  mpa_writer->startMap();
  writeMsRun(*(msrun_peptide_list.getMsRunSp().get()));


  mpa_writer->append("peptide_measurements");
  mpa_writer->startMap(size);
  if(is_mbr)
    {
      for(const masschroq::MbrPeptideMeasurementsSp &measures :
          msrun_peptide_list.getMbrPeptideMeasurementsList())
        {
          writeMbrPeptideMeasurements(*(measures.get()),
                                      msrun_peptide_list.getMsRunRetentionTimeConstPtr());

          monitor.count();
        }
    }
  else
    {
      for(const masschroq::PeptideMeasurementsSp &measures :
          msrun_peptide_list.getPeptideMeasurementsList())
        {
          writePeptideMeasurements(*(measures.get()),
                                   msrun_peptide_list.getMsRunRetentionTimeConstPtr());

          monitor.count();
        }
    }
  monitor.setTotalSteps(0);
  mpa_writer->endMap();

  mpa_writer->endMap();
}

void
masschroq::CborOutputStream::writeVectorDouble(const std::vector<double> &vector)
{
  mpa_writer->startArray(vector.size());
  for(double x : vector)
    {
      mpa_writer->append(x);
    }
  mpa_writer->endArray();
}

void
masschroq::CborOutputStream::writeProjectParameters(const ProjectParameters &project_parameters)
{
  project_parameters.writeParameters(*mpa_writer);
}

void
masschroq::CborOutputStream::writeMsRunRetentionTime(
  const Trace &report_common_ms2, const MsRunRetentionTime<QString> &msrun_retention_time)
{
  mpa_writer->startMap(5);
  mpa_writer->append(QLatin1String("original"));
  writeVectorDouble(msrun_retention_time.getMs1RetentionTimeVector());
  mpa_writer->append(QLatin1String("aligned"));
  writeVectorDouble(msrun_retention_time.getAlignedRetentionTimeVector());
  mpa_writer->append(QLatin1String("ms2_delta_rt"));
  writeTrace(report_common_ms2);

  Trace median(report_common_ms2);
  msrun_retention_time.getMs2MedianFilter().filter(median);

  mpa_writer->append(QLatin1String("ms2_median"));
  writeVectorDouble(median.yValues());

  msrun_retention_time.getMs2MeanFilter().filter(median);
  mpa_writer->append(QLatin1String("ms2_mean"));
  writeVectorDouble(median.yValues());


  mpa_writer->endMap();
}

} // namespace pappso
