/**
 * \file src/cli/export/cborstreamreaderbase.cpp
 * \date 11/02/2025
 * \author Olivier Langella
 * \brief read cbor stream from masschroq3
 */

/*******************************************************************************
 * 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 "cborstreamreaderbase.h"
#include <pappsomspp/core/pappsoexception.h>

CborStreamReaderBase::CborStreamReaderBase()
{
}

CborStreamReaderBase::~CborStreamReaderBase()
{
}

void
CborStreamReaderBase::startGroup()
{
}

void
CborStreamReaderBase::readCbor(QFile *cborp,
                               pappso::UiMonitorInterface &monitor)
{
  qDebug();
  initCborReader(cborp);

  qDebug();
  if(mpa_cborReader->isMap())
    {
      readRoot(monitor);
    }
  qDebug();
}

void
CborStreamReaderBase::readInformations(pappso::UiMonitorInterface &monitor)
{
  qDebug();
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readQuantificationData(
  pappso::UiMonitorInterface &monitor)
{
  qDebug();
  mpa_cborReader->enterContainer();


  while(!mpa_cborReader->lastError() && mpa_cborReader->hasNext())
    {
      readQuantificationDataObject(monitor);
      // m_cborReader.next();
      //  dumpOne(monitor, 0);
      //  skipCurrentElement();
    }
  qDebug();
  mpa_cborReader->leaveContainer();
}


void
CborStreamReaderBase::checkError(const QString &element_name)
{
  if(mpa_cborReader->type() == QCborStreamReader::Invalid)
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: %1 in %2")
          .arg(mpa_cborReader->lastError().toString())
          .arg(element_name));
    }
}
void
CborStreamReaderBase::readRoot(pappso::UiMonitorInterface &monitor)
{

  qDebug();
  mpa_cborReader->enterContainer();

  getExpectedString();
  if(m_expectedString == "informations")
    {
      readInformations(monitor);
    }
  else
    {
      throw pappso::PappsoException("ERROR: expecting informations element");
    }


  qDebug();
  getExpectedString();
  if(m_expectedString == "project_parameters")
    {
      readProjectParameters();
    }
  else
    {
      throw pappso::PappsoException(
        "ERROR: expecting project_parameters element");
    }

  qDebug();

  getExpectedString();
  if(m_expectedString == "masschroq_methods")
    {
      readMasschroqMethods(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        "ERROR: expecting masschroq_methods element");
    }

  qDebug();
  getExpectedString();
  if(m_expectedString == "identification_data")
    {
      readIdentificationData(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        "ERROR: expecting identification_data element");
    }

  qDebug();
  getExpectedString();
  if(m_expectedString == "actions")
    {
      readActions(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting actions element not %1")
          .arg(m_expectedString));
    }

  qDebug();
  getExpectedString();
  if(m_expectedString == "alignment_data")
    {
      readAlignmentDataArray(monitor);
      getExpectedString();
    }

  if(m_expectedString == "quantification_data")
    {
      readQuantificationData(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting quantification_data element not %1")
          .arg(m_expectedString));
    }

  qDebug();
  getExpectedString();
  if(m_expectedString == "end")
    {
      readEnd(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting end element not %1")
          .arg(m_expectedString));
    }
  qDebug();
}

void
CborStreamReaderBase::readAlignmentDataArray(
  pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->enterContainer(); // array
  while(!mpa_cborReader->lastError() && mpa_cborReader->hasNext())
    {
      readAlignmentData(monitor);
    }
  mpa_cborReader->leaveContainer(); // array
}


void
CborStreamReaderBase::readAlignment()
{
  // m_cborReader.next();
  if(!mpa_cborReader->enterContainer())
    {
      checkError("alignment");
    }
  getExpectedString();
  if(m_expectedString == "msrun_ref")
    {
      getExpectedString();
      qDebug() << m_expectedString;
      m_msrunReference = m_expectedString;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting msrun_ref element not %1")
          .arg(m_expectedString));
    }
  getExpectedString();
  if(m_expectedString == "corrections")
    {

      if(!mpa_cborReader->enterContainer())
        {
          checkError("corrections");
        }
      while(getExpectedString())
        {
          m_msrunId = m_expectedString;
          mpa_cborReader->enterContainer();

          std::vector<double> original_rt;
          std::vector<double> aligned_rt;
          while(getExpectedString())
            {
              if(m_expectedString == "original")
                {
                  mpa_cborReader->readArray(original_rt);
                }
              else if(m_expectedString == "aligned")
                {
                  mpa_cborReader->readArray(aligned_rt);
                }
              else
                {
                  mpa_cborReader->next();
                }
            }
          pappso::MsRunRetentionTime<QString> rt_line(original_rt);
          rt_line.setAlignedRetentionTimeVector(aligned_rt);
          reportMsRunRetentionTime(rt_line);
          mpa_cborReader->leaveContainer();
        }
      mpa_cborReader->leaveContainer();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting corrections element not %1")
          .arg(m_expectedString));
    }
  mpa_cborReader->leaveContainer();
  qDebug();
}

void
CborStreamReaderBase::reportMsRunRetentionTime(
  const pappso::MsRunRetentionTime<QString> &rt_line [[maybe_unused]])
{
}

void
CborStreamReaderBase::readAlignmentData(pappso::UiMonitorInterface &monitor)
{
  // m_cborReader.next();
  if(!mpa_cborReader->enterContainer())
    {
      checkError("alignment_data");
    }
  qDebug() << m_expectedString;
  getExpectedString();
  if(m_expectedString == "alignment_id")
    {
      getExpectedString();
      qDebug() << m_expectedString;
      m_alignmentId = m_expectedString;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting alignment_id element not %1")
          .arg(m_expectedString));
    }
  getExpectedString();
  if(m_expectedString == "group_id")
    {
      getExpectedString();
      m_groupId = m_expectedString;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting group_id element not %1")
          .arg(m_expectedString));
    }
  getExpectedString();
  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }

  if(m_expectedString == "alignment")
    {
      readAlignment();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting alignment element not %1")
          .arg(m_expectedString));
    }
  getExpectedString();
  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }
  mpa_cborReader->leaveContainer();
  qDebug();
}


void
CborStreamReaderBase::readQuantificationDataObject(
  pappso::UiMonitorInterface &monitor)
{

  qDebug();
  mpa_cborReader->enterContainer();
  getExpectedString();
  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }

  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }
  if(m_expectedString == "quantify_id")
    {
      getExpectedString();
      m_quantificationId = m_expectedString;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting quantify_id element not %1")
          .arg(m_expectedString));
    }

  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }

  getExpectedString();
  if(m_expectedString == "group_id")
    {
      getExpectedString();
      m_groupId = m_expectedString;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting group_id element not %1")
          .arg(m_expectedString));
    }

  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }

  startGroup();
  getExpectedString();

  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }
  if(m_expectedString == "first_pass")
    {
      startFirstPass();
      readGroupQuantificationPass(monitor);
      stopFirstPass();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting first_pass element not %1")
          .arg(m_expectedString));
    }

  getExpectedString();

  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }

  if(m_expectedString == "second_pass")
    {
      startSecondPass();
      readGroupQuantificationPass(monitor);
      stopSecondPass();
    }
  else
    {
      mpa_cborReader->next();
    }

  getExpectedString();
  if(m_expectedString.startsWith("timestamp"))
    {
      mpa_cborReader->next();
      getExpectedString();
    }
  qDebug();
  // skipCurrentElement();
  qDebug();
  // skipCurrentElement();
  mpa_cborReader->leaveContainer();
  qDebug();
  stopGroup();
}

void
CborStreamReaderBase::readGroupQuantificationPass(
  pappso::UiMonitorInterface &monitor)
{
  qDebug();

  std::size_t size = mpa_cborReader->length();
  std::size_t i    = 0;
  mpa_cborReader->enterContainer();
  while(getExpectedString())
    {
      m_msrunId = m_expectedString;
      readQrDataBlock(monitor);
      i++;
      // m_cborReader.next();
    }

  if(i < size)
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: only %1 msruns out of %2 expected, "
                    "groupid %3")
          .arg(i)
          .arg(size)
          .arg(m_groupId));
    }
  qDebug();
  // skipCurrentElement();
  qDebug();
  // skipCurrentElement();
  mpa_cborReader->leaveContainer();
  qDebug();
}


void
CborStreamReaderBase::readQrDataBlock(pappso::UiMonitorInterface &monitor)
{
  qDebug();
  mpa_cborReader->enterContainer();
  getExpectedString();
  if(m_expectedString == "msrun")
    {
      readMsrun(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting msrun element not %1")
          .arg(m_expectedString));
    }
  // retention_time_correction
  getExpectedString();
  if(m_expectedString == "retention_time_correction")
    {
      mpa_cborReader->next();
      getExpectedString();
    }
  if(m_expectedString == "peptide_measurements")
    {
      readPeptideMeasurements(monitor);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: expecting peptide_measurements element not %1")
          .arg(m_expectedString));
    }
  qDebug();
  // skipCurrentElement();
  mpa_cborReader->leaveContainer();
}


void
CborStreamReaderBase::readMsrun(pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->enterContainer();
  m_msrunId = "";
  while(getExpectedString())
    {
      if(m_expectedString == "id")
        {
          getExpectedString();
          m_msrunId = m_expectedString;
        }

      else if(m_expectedString == "filename")
        {
          getExpectedString();
          m_msrunFileName = m_expectedString;
        }
      else if(m_expectedString == "sample")
        {
          getExpectedString();
          m_msrunSampleName = m_expectedString;
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR: unexpected element %1 in msrun")
              .arg(m_expectedString));
        }
    }
  reportMsRun();
  mpa_cborReader->leaveContainer();
}

void
CborStreamReaderBase::readPeptideMeasurements(
  pappso::UiMonitorInterface &monitor)
{
  qDebug() << mpa_cborReader->length();
  monitor.setTotalSteps(mpa_cborReader->length());


  if(!mpa_cborReader->isLengthKnown())
    {
      throw pappso::PappsoException(
        QObject::tr(
          "ERROR: peptide_measurements map size not known in peptideid %3")
          .arg(m_peptideId));
    }
  std::size_t size = mpa_cborReader->length();
  mpa_cborReader->enterContainer(); // map
  std::size_t i = 0;
  while(getExpectedString())
    {
      qDebug() << m_expectedString;
      m_peptideId = m_expectedString;
      readPeptideMeasurement(monitor);
      i++;
      // mpa_cborReader->next();
    }

  qDebug() << size << " " << i;
  if(i < size)
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: only %1 peptide_measurements out of %2 expected, "
                    "peptideid %3")
          .arg(i)
          .arg(size)
          .arg(m_peptideId));
    }
  mpa_cborReader->leaveContainer();
  qDebug() << size;
}

void
CborStreamReaderBase::readPeptideMeasurement(
  pappso::UiMonitorInterface &monitor)
{
  qDebug();
  mpa_cborReader->enterContainer();

  m_rtTarget = 0;
  m_mods     = "";
  while(getExpectedString())
    {
      if(m_expectedString == "proforma")
        {
          getExpectedString();
          m_proForma = m_expectedString;
        }
      else if(m_expectedString == "mods")
        {
          getExpectedString();
          m_mods = m_expectedString;
        }
      else if(m_expectedString == "rt_target")
        {

          if(mpa_cborReader->isDouble())
            {
              m_rtTarget = mpa_cborReader->toDouble();
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting double for rt_target"));
            }
          mpa_cborReader->next();
        }
      else if(m_expectedString == "xics")
        {
          if(!mpa_cborReader->isLengthKnown())
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: xics array size not known in peptideid %3")
                  .arg(m_peptideId));
            }
          std::size_t size = mpa_cborReader->length();
          std::size_t i    = 0;
          mpa_cborReader->enterContainer(); // array


          while(!mpa_cborReader->lastError() && mpa_cborReader->hasNext())
            {
              qDebug();
              readXic(monitor);
              i++;
              // mpa_cborReader->next();

              qDebug();
            }
          if(i < size)
            {
              throw pappso::PappsoException(
                QObject::tr(
                  "ERROR: only %1 xics out of %2 expected, peptideid %3")
                  .arg(i)
                  .arg(size)
                  .arg(m_peptideId));
            }
          qDebug();
          mpa_cborReader->leaveContainer();
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR: unexpected element %1 in PeptideMeasurement")
              .arg(m_expectedString));
        }
    }
  qDebug();
  mpa_cborReader->leaveContainer();
}

void
CborStreamReaderBase::readXic(pappso::UiMonitorInterface &monitor)
{
  qDebug();
  // dumpOne(monitor, 0);
  if(!mpa_cborReader->enterContainer())
    {
      checkError("xic");
    }
  m_charge  = 0;
  m_mz      = 0;
  m_isotope = 0;
  m_rank    = 0;
  m_thRatio = 0;
  m_quality = "";
  m_label   = "";
  PeakStruct peak;
  while(getExpectedString())
    {
      if(m_expectedString == "charge")
        {
          if(mpa_cborReader->isInteger())
            {
              m_charge = mpa_cborReader->toInteger();
              qDebug() << "m_charge=" << m_charge;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting integer for charge"));
            }

          mpa_cborReader->next();
        }
      else if(m_expectedString == "mz")
        {

          if(mpa_cborReader->isDouble())
            {
              m_mz = mpa_cborReader->toDouble();
              qDebug() << "m_mz=" << m_mz;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting double for mz"));
            }
          mpa_cborReader->next();
        }
      else if(m_expectedString == "xic_coord")
        {
          mpa_cborReader->next();
        }
      else if(m_expectedString == "isotope")
        {
          if(mpa_cborReader->isInteger())
            {
              m_isotope = mpa_cborReader->toInteger();
              qDebug() << "m_isotope=" << m_isotope;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting integer for isotope"));
            }
          mpa_cborReader->next();
        }

      else if(m_expectedString == "rank")
        {
          if(mpa_cborReader->isInteger())
            {
              m_rank = mpa_cborReader->toInteger();
              qDebug() << "m_rank=" << m_rank;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting integer for rank"));
            }
          mpa_cborReader->next();
        }
      else if(m_expectedString == "th_ratio")
        {
          if(mpa_cborReader->isDouble())
            {
              m_thRatio = mpa_cborReader->toDouble();
              qDebug() << "th_ratio=" << m_thRatio;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting double for m_thRatio"));
            }
          mpa_cborReader->next();
        }
      else if(m_expectedString == "quality")
        {

          getExpectedString();
          m_quality = m_expectedString;
        }
      else if(m_expectedString == "label")
        {

          getExpectedString();
          m_label = m_expectedString;
        }


      else if(m_expectedString == "peak")
        {
          peak = readPeak(monitor);
        }
      else if(m_expectedString == "trace")
        {
          readTrace();
        }
      else if(m_expectedString == "peak_shape")
        {
          readPeakShape();
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR: unexpected element %1 in Xic")
              .arg(m_expectedString));
        }
    }
  reportPeakLine(peak);
  mpa_cborReader->leaveContainer();
}


CborStreamReaderBase::PeakStruct
CborStreamReaderBase::readPeak(pappso::UiMonitorInterface &monitor)
{
  qDebug();
  // dumpOne(monitor, 0);
  PeakStruct peak;
  mpa_cborReader->enterContainer();
  while(getExpectedString())
    {
      qDebug() << m_expectedString;
      if(m_expectedString == "area")
        {
          if(mpa_cborReader->isDouble())
            {
              peak.area = mpa_cborReader->toDouble();
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting double for area"));
            }
          mpa_cborReader->next();
        }
      else if(m_expectedString == "max_intensity")
        {
          // dumpOne(monitor, 0);
          if(mpa_cborReader->isDouble())
            {
              peak.max_intensity = mpa_cborReader->toDouble();
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("ERROR: expecting double for max_intensity"));
            }
          mpa_cborReader->next();

          checkError("peak max_intensity");
        }

      else if(m_expectedString == "rt")
        {
          // dumpOne(monitor, 0);
          mpa_cborReader->enterContainer();
          std::size_t i = 0;

          while(!mpa_cborReader->lastError() && mpa_cborReader->hasNext())
            {
              qDebug() << i;
              // if(mpa_cborReader->isDouble())
              //{
              peak.rt[i] = mpa_cborReader->toDouble();
              qDebug() << peak.rt[i];
              mpa_cborReader->next();
              i++;
              //}
            }
          mpa_cborReader->leaveContainer();
        }
      else if(m_expectedString == "aligned_rt")
        {
          // dumpOne(monitor, 0);
          mpa_cborReader->enterContainer();
          std::size_t i = 0;

          while(!mpa_cborReader->lastError() && mpa_cborReader->hasNext())
            {
              qDebug() << i;
              // if(mpa_cborReader->isDouble())
              //{
              peak.aligned_rt[i] = mpa_cborReader->toDouble();
              qDebug() << peak.rt[i];
              mpa_cborReader->next();
              i++;
              //}
            }
          mpa_cborReader->leaveContainer();
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR: unexpected element %1 in peak")
              .arg(m_expectedString));
        }
    }
  mpa_cborReader->leaveContainer();
  qDebug();
  return peak;
}

void
CborStreamReaderBase::reportPeakLine(
  const CborStreamReaderBase::PeakStruct &peak)
{
}

void
CborStreamReaderBase::readTrace()
{
  mpa_cborReader->next();
}
void
CborStreamReaderBase::readPeakShape()
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readPappsoTrace(pappso::Trace &trace)
{
  if(!mpa_cborReader->enterContainer())
    {
      checkError("trace");
    }
  getExpectedString();
  std::vector<double> x, y;
  if(m_expectedString == "x")
    {
      mpa_cborReader->readArray(x);
    }

  getExpectedString();
  if(m_expectedString == "y")
    {
      mpa_cborReader->readArray(y);
    }
  trace.initialize(x, y);
  mpa_cborReader->leaveContainer();
}


void
CborStreamReaderBase::readProjectParameters()
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readActions(pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readIdentificationData(
  pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readMasschroqMethods(pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::readEnd(pappso::UiMonitorInterface &monitor)
{
  mpa_cborReader->next();
}

void
CborStreamReaderBase::reportMsRun()
{
}


void
CborStreamReaderBase::startFirstPass()
{
}

void
CborStreamReaderBase::startSecondPass()
{
}

void
CborStreamReaderBase::stopFirstPass()
{
}

void
CborStreamReaderBase::stopSecondPass()
{
}

void
CborStreamReaderBase::stopGroup()
{
}
