/**
 * \file mcql/input/cborstreamreadertsv.cpp
 * \date 04/01/2025
 * \author Olivier Langella
 * \brief read cbor stream for tsv output
 */

/*******************************************************************************
 * 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 "cborstreamreadertsv.h"
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include <pappsomspp/core/processing/uimonitor/uimonitorvoid.h>
#include <QCborValue>
#include <pappsomspp/core/processing/detection/tracedetectionzivy.h>
#include <pappsomspp/core/masschroq/alignmentmethod.h>
#include <pappsomspp/core/masschroq/quantificationmethod.h>


mcql::CborStreamReaderTsvBase::CborStreamReaderTsvBase()
{
  qDebug();
}

mcql::CborStreamReaderTsvBase::~CborStreamReaderTsvBase()
{
  qDebug();
  mpa_calcWriterInterface->close();

  delete mpa_calcWriterInterface;
}


mcql::CborStreamReaderOds::CborStreamReaderOds(const QString &ods_file)
{

  try
    {
      OdsDocWriter *ods_p     = new OdsDocWriter(ods_file);
      mpa_calcWriterInterface = ods_p;
    }
  catch(OdsException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error writing ODS file %1 :\n%2")
          .arg(ods_file)
          .arg(error.qwhat()));
    }
}
mcql::CborStreamReaderOds::~CborStreamReaderOds()
{
  qDebug();
}


mcql::CborStreamReaderTsv::CborStreamReaderTsv(const QString &output_directory)
{

  qDebug();
  try
    {
      TsvDirectoryWriter *mpa_tsv =
        new TsvDirectoryWriter(QDir(output_directory));
      mpa_tsv->setFlushLines(true);
      mpa_calcWriterInterface = mpa_tsv;
    }
  catch(OdsException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error writing TSV ouput in directory %1 :\n%2")
          .arg(output_directory)
          .arg(error.qwhat()));
    }
  qDebug();
}

mcql::CborStreamReaderTsv::~CborStreamReaderTsv()
{
}


void
mcql::CborStreamReaderTsvBase::setNoMissedPeak(bool no_missed_peak)
{
  m_isMissedPeaks = !no_missed_peak;
}


void
mcql::CborStreamReaderTsvBase::skipCurrentElement()
{
  mpa_cborReader->next();
  /*
  if(mpa_cborReader->isContainer())
    {
      mpa_cborReader->enterContainer();

      while(mpa_cborReader->hasNext())
        {
          mpa_cborReader->next();
        }
      mpa_cborReader->leaveContainer();
    }
  else
    {

      mpa_cborReader->next();
    }*/
}


void
mcql::CborStreamReaderTsvBase::readInformations(
  pappso::UiMonitorInterface &monitor)
{
  qDebug();
  mpa_cborReader->enterContainer();
  mpa_calcWriterInterface->writeSheet("Informations");
  bool is_ok = getExpectedString();
  while(is_ok)
    {
      mpa_calcWriterInterface->writeCell(m_expectedString);
      if(m_expectedString == "cpu_used")
        {
          mpa_calcWriterInterface->writeCell((int)mpa_cborReader->toInteger());
          mpa_cborReader->next();
        }
      else
        {
          is_ok = getExpectedString();
          if(is_ok)
            {

              mpa_calcWriterInterface->writeCell(m_expectedString);
            }
        }

      mpa_calcWriterInterface->writeLine();

      if(is_ok == false)
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR writing Informations sheet"));
        }
      is_ok = getExpectedString();
    }

  mpa_cborReader->leaveContainer();
  qDebug();
}


void
mcql::CborStreamReaderTsvBase::readIdentificationData(
  pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->enterContainer();

  getExpectedString();
  if(m_expectedString == "msrun_list")
    {
      skipCurrentElement();
    }
  else
    {
      throw pappso::PappsoException("ERROR: expecting msrun_list element");
    }
  getExpectedString();
  if(m_expectedString == "protein_list")
    {
      qDebug() << mpa_cborReader->length();
      // throw pappso::PappsoException("ERROR: expecting msrun_list element");
      mpa_cborReader->enterContainer();
      while(getExpectedString())
        {
          QString protein_id = m_expectedString;
          qDebug() << protein_id;

          mpa_cborReader->enterContainer();
          while(getExpectedString())
            {
              qDebug() << m_expectedString;
              if(m_expectedString == "description")
                {
                  // qDebug() << m_expectedString;
                  if(getExpectedString())
                    {

                      qDebug() << m_expectedString;
                      m_proteinMap.insert({protein_id, m_expectedString});
                    }
                }
              else
                {
                  skipCurrentElement();
                }
            }
          mpa_cborReader->leaveContainer();
        }
      mpa_cborReader->leaveContainer();
    }
  else
    {
      throw pappso::PappsoException("ERROR: expecting protein_list element");
    }

  getExpectedString();
  if(m_expectedString == "peptide_list")
    {
      writePeptideProteinList();
    }
  else
    {
      throw pappso::PappsoException("ERROR: expecting peptide_list element");
    }
  // peptide_list
  // protein_list

  getExpectedString();
  if(m_expectedString == "msrunpeptide_list")
    {
      skipCurrentElement();
    }
  else
    {
      throw pappso::PappsoException(
        "ERROR: expecting msrunpeptide_list element");
    }
  // mpa_cborReader->enterContainer();
  // dumpOne(monitor, 0);

  mpa_cborReader->leaveContainer();
}


void
mcql::CborStreamReaderTsvBase::writePeptideProteinList()
{
  qDebug();
  mpa_calcWriterInterface->writeSheet("proteins");
  // mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("peptide");
  mpa_calcWriterInterface->writeCell("ProForma");
  mpa_calcWriterInterface->writeCell("protein");
  mpa_calcWriterInterface->writeCell("protein_description");

  OdsTableSettings settings;
  settings.setVerticalSplit(1);
  mpa_calcWriterInterface->setCurrentOdsTableSettings(settings);


  if(m_expectedString == "peptide_list")
    {
      mpa_cborReader->enterContainer();
      while(getExpectedString())
        {
          QString peptide_id = m_expectedString;
          qDebug() << peptide_id;

          //"proforma" : "SLTNDWEDHLAVK",
          //             "proteins" : ["prota1a1"],

          mpa_cborReader->enterContainer();
          QString proforma = "";
          QStringList protein_list_str;
          while(getExpectedString())
            {
              qDebug() << m_expectedString;
              if(m_expectedString == "proforma")
                {
                  // qDebug() << m_expectedString;
                  if(getExpectedString())
                    {
                      proforma = m_expectedString;
                    }
                }
              else if(m_expectedString == "proteins")
                {
                  mpa_cborReader->enterContainer();
                  while(getExpectedString())
                    {
                      protein_list_str << m_expectedString;
                    }
                  mpa_cborReader->leaveContainer();
                }
              else
                {
                  skipCurrentElement();
                }
            }

          mpa_cborReader->leaveContainer();

          for(auto protein_id : protein_list_str)
            {
              mpa_calcWriterInterface->writeLine();
              mpa_calcWriterInterface->writeCell(peptide_id);
              mpa_calcWriterInterface->writeCell(proforma);
              mpa_calcWriterInterface->writeCell(protein_id);
              auto it = m_proteinMap.find(protein_id);
              if(it == m_proteinMap.end())
                {
                  throw pappso::ExceptionNotFound(
                    QObject::tr("protein %1 not found").arg(protein_id));
                }
              mpa_calcWriterInterface->writeCell(it->second);
            }
        }
      mpa_cborReader->leaveContainer();
    }

  qDebug();
}

void
mcql::CborStreamReaderTsvBase::reportPeakLine(
  const mcql::CborStreamReaderTsvBase::PeakStruct &peak)
{
  if(!m_isMissedPeaks)
    {
      if(m_quality == "missed")
        return;
    }
  bool is_aligned = true;
  if(peak.aligned_rt[1] == 0)
    is_aligned = false;
  mpa_calcWriterInterface->writeCell(m_quantificationId);
  mpa_calcWriterInterface->writeCell(m_groupId);
  mpa_calcWriterInterface->writeCell(m_msrunId);
  // mpa_calcWriterInterface->writeCell(m_msrunFileName);
  mpa_calcWriterInterface->writeCell(m_msrunSampleName);
  mpa_calcWriterInterface->writeCell(m_mz);
  if(is_aligned)
    mpa_calcWriterInterface->writeCell(peak.aligned_rt[1]);
  else
    mpa_calcWriterInterface->writeCell(peak.rt[1]);
  mpa_calcWriterInterface->writeCell(peak.max_intensity);
  mpa_calcWriterInterface->writeCell(peak.area);
  mpa_calcWriterInterface->writeCell(m_quality);

  if(is_aligned)
    {
      mpa_calcWriterInterface->writeCell(peak.aligned_rt[0]);
      mpa_calcWriterInterface->writeCell(peak.aligned_rt[2]);
    }
  else
    {
      mpa_calcWriterInterface->writeCell(peak.rt[0]);
      mpa_calcWriterInterface->writeCell(peak.rt[2]);
    }
  mpa_calcWriterInterface->writeCell(peak.rt[0]);
  mpa_calcWriterInterface->writeCell(peak.rt[1]);
  mpa_calcWriterInterface->writeCell(peak.rt[2]);
  mpa_calcWriterInterface->writeCell(m_peptideId);
  mpa_calcWriterInterface->writeCell(m_label);
  mpa_calcWriterInterface->writeCell(m_proForma);
  mpa_calcWriterInterface->writeCell(m_charge);
  mpa_calcWriterInterface->writeCell(m_mods);

  if(m_niMinimumAbundance != 0)
    {
      mpa_calcWriterInterface->writeCell(m_isotope);
      mpa_calcWriterInterface->writeCell(m_rank);
      mpa_calcWriterInterface->writeCell(m_thRatio);
    }
  mpa_calcWriterInterface->writeLine();
}

void
mcql::CborStreamReaderTsvBase::writeQuantificationSheet()
{

  OdsTableSettings settings;
  settings.setVerticalSplit(1);
  mpa_calcWriterInterface->setCurrentOdsTableSettings(settings);

  mpa_calcWriterInterface->setCellAnnotation("quantification XML id");
  mpa_calcWriterInterface->writeCell("quantification");
  mpa_calcWriterInterface->setCellAnnotation("group XML id (fraction name)");
  mpa_calcWriterInterface->writeCell("group");
  mpa_calcWriterInterface->setCellAnnotation("MS run XML id (sample id)");
  mpa_calcWriterInterface->writeCell("msrun");
  mpa_calcWriterInterface->setCellAnnotation("MS run file path");
  mpa_calcWriterInterface->writeCell("msrunfile");
  mpa_calcWriterInterface->setCellAnnotation("XIC m/z");
  mpa_calcWriterInterface->writeCell("mz");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak maximum intensity retention time aligned");
  mpa_calcWriterInterface->writeCell("rt");
  mpa_calcWriterInterface->setCellAnnotation("peak maximum intensity");
  mpa_calcWriterInterface->writeCell("maxintensity");
  mpa_calcWriterInterface->setCellAnnotation("peak area");
  mpa_calcWriterInterface->writeCell("area");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak quality : how the peak was measured");
  mpa_calcWriterInterface->writeCell("peak quality");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak start retention time aligned");
  mpa_calcWriterInterface->writeCell("rtbegin");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak stop retention time aligned");
  mpa_calcWriterInterface->writeCell("rtend");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak start retention time not aligned");
  mpa_calcWriterInterface->writeCell("realrtbegin");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak apex (maximum intensity) retention time not aligned");
  mpa_calcWriterInterface->writeCell("realrtapex");
  mpa_calcWriterInterface->setCellAnnotation(
    "peak stop retention time not aligned");
  mpa_calcWriterInterface->writeCell("realrtend");
  mpa_calcWriterInterface->setCellAnnotation("peptide id");
  mpa_calcWriterInterface->writeCell("peptide");
  mpa_calcWriterInterface->setCellAnnotation("isotope label");
  mpa_calcWriterInterface->writeCell("label");
  mpa_calcWriterInterface->setCellAnnotation("peptide sequence");
  mpa_calcWriterInterface->writeCell("sequence");
  mpa_calcWriterInterface->setCellAnnotation("peptide charge");
  mpa_calcWriterInterface->writeCell("z");
  mpa_calcWriterInterface->setCellAnnotation(
    "peptide modifications (free text)");
  mpa_calcWriterInterface->writeCell("mods");
  if(m_niMinimumAbundance != 0)
    {
      mpa_calcWriterInterface->setCellAnnotation("natural isotope number");
      mpa_calcWriterInterface->writeCell("ninumber");
      mpa_calcWriterInterface->setCellAnnotation("natural isotope rank");
      mpa_calcWriterInterface->writeCell("nirank");
      mpa_calcWriterInterface->setCellAnnotation(
        "natural isotope theoretical ratio");
      mpa_calcWriterInterface->writeCell("niratio");
    }


  mpa_calcWriterInterface->writeLine();
}


void
mcql::CborStreamReaderTsvBase::startGroup()
{
  qDebug();
  mpa_calcWriterInterface->writeSheet(QString("peptides_%1").arg(m_groupId));
  writeQuantificationSheet();
  m_quantifiedGroupIdList.push_back(m_groupId);
}


void
mcql::CborStreamReaderTsvBase::readProjectParameters()
{

  QCborValue parameters;
  parameters = parameters.fromCbor(*mpa_cborReader);
  pappso::ProjectParameters all_params(parameters.toJsonValue().toObject());
  m_projectParameters.merge(all_params);

  m_projectParameters.writeParameters(*mpa_calcWriterInterface);
}


void
mcql::CborStreamReaderTsvBase::writeQuantificationMethod(
  pappso::masschroq::QuantificationMethod *p_quantification_method)
{

  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("XIC parameters");
  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("integration");
  if(p_quantification_method->getXicExtractMethod() ==
     pappso::Enums::XicExtractMethod::max)
    mpa_calcWriterInterface->writeCell("max");
  if(p_quantification_method->getXicExtractMethod() ==
     pappso::Enums::XicExtractMethod::sum)
    mpa_calcWriterInterface->writeCell("sum");
  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("extraction range lower limit");
  mpa_calcWriterInterface->writeCell(
    p_quantification_method->getXicExtractionLowerPrecisionPtr()->toString());
  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("extraction range upper limit");
  mpa_calcWriterInterface->writeCell(
    p_quantification_method->getXicExtractionUppersPrecisionPtr()->toString());
  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeLine();


  mpa_calcWriterInterface->writeCell("Filters");
  mpa_calcWriterInterface->writeCell(
    p_quantification_method->getXicFilter().get()->toString());
  mpa_calcWriterInterface->writeLine();


  mpa_calcWriterInterface->writeCell("Detection method");
  mpa_calcWriterInterface->writeLine();
  const pappso::TraceDetectionInterface *p_detection =
    p_quantification_method->getTraceDetectionInterfaceCstSPtr().get();
  if(p_detection != nullptr)
    {
      const pappso::TraceDetectionZivy *p_detectionZivy =
        dynamic_cast<const pappso::TraceDetectionZivy *>(p_detection);
      if(p_detectionZivy != nullptr)
        {
          mpa_calcWriterInterface->writeCell("detection Zivy");
          mpa_calcWriterInterface->writeLine();
          mpa_calcWriterInterface->writeCell("smoothing half edge window");
          mpa_calcWriterInterface->writeCell(
            (std::size_t)p_detectionZivy->getSmoothingHalfEdgeWindows());
          mpa_calcWriterInterface->writeLine();
          mpa_calcWriterInterface->writeCell("maxmin half edge window");
          mpa_calcWriterInterface->writeCell(
            (std::size_t)p_detectionZivy->getMaxMinHalfEdgeWindows());
          mpa_calcWriterInterface->writeLine();
          mpa_calcWriterInterface->writeCell("minmax half edge window");
          mpa_calcWriterInterface->writeCell(
            (std::size_t)p_detectionZivy->getMinMaxHalfEdgeWindows());
          mpa_calcWriterInterface->writeLine();
          mpa_calcWriterInterface->writeCell("detection threshold on maxmin");
          mpa_calcWriterInterface->writeCell(
            (std::size_t)p_detectionZivy->getDetectionThresholdOnMaxmin());
          mpa_calcWriterInterface->writeLine();
          mpa_calcWriterInterface->writeCell("detection threshold on minmax");
          mpa_calcWriterInterface->writeCell(
            (std::size_t)p_detectionZivy->getDetectionThresholdOnMinmax());
          mpa_calcWriterInterface->writeLine();
        }
    }


  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeLine();
}

void
mcql::CborStreamReaderTsvBase::writeAlignmentMethod(
  pappso::masschroq::AlignmentMethod *p_alignment_method)
{

  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeLine();
  mpa_calcWriterInterface->writeCell("Alignment method");
  mpa_calcWriterInterface->writeLine();
  if(p_alignment_method != nullptr)
    {
      mpa_calcWriterInterface->writeCell("MS1 smoothing half window");
      mpa_calcWriterInterface->writeCell(
        p_alignment_method->getMs1SmoothingWindow());
      mpa_calcWriterInterface->writeLine();
      mpa_calcWriterInterface->writeCell("MS2 smoothing half window");
      mpa_calcWriterInterface->writeCell(
        p_alignment_method->getMs2SmoothingWindow());
      mpa_calcWriterInterface->writeLine();
      mpa_calcWriterInterface->writeCell("MS2 tendency half window");
      mpa_calcWriterInterface->writeCell(
        p_alignment_method->getMs2TendencyWindow());
      mpa_calcWriterInterface->writeLine();
    }
}

void
mcql::CborStreamReaderTsvBase::readMasschroqMethods(
  pappso::UiMonitorInterface &monitor)
{
  qDebug();
  pappso::masschroq::AlignmentMethodSp msp_alignmentMethod;
  pappso::masschroq::QuantificationMethodSp msp_quantificationMethod;

  QJsonValue methods = QCborValue::fromCbor(*mpa_cborReader).toJsonValue();
  msp_alignmentMethod =
    std::make_shared<pappso::masschroq::AlignmentMethod>("a1");
  msp_alignmentMethod.get()->setJsonObject(
    methods.toObject().value("alignment_method").toObject());
  msp_quantificationMethod =
    std::make_shared<pappso::masschroq::QuantificationMethod>("q1");
  msp_quantificationMethod.get()->setJsonObject(
    methods.toObject().value("quantification_method").toObject());
  writeAlignmentMethod(msp_alignmentMethod.get());
  writeQuantificationMethod(msp_quantificationMethod.get());

  m_niMinimumAbundance =
    msp_quantificationMethod.get()->getIsotopeMinimumRatio();

  qDebug();
}

const std::vector<QString> &
mcql::CborStreamReaderTsvBase::getQuantifiedGroupIdList() const
{
  return m_quantifiedGroupIdList;
}
