/**
 * \file pappsomspp/core/processing/cbor/mzcbor/mzcborspectrumcollectionreader.cpp
 * \date 22/11/2025
 * \author Olivier Langella
 * \brief read mzcbor for spectrum collection m_SpectrumCollectionHandlerInterface
 */

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


#include "mzcborspectrumcollectionreader.h"
#include "spectrum.h"
#include "pappsomspp/core/pappsoexception.h"
#include <qlogging.h>
#include <qobject.h>


pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::MzcborSpectrumCollectionReader(
  const MsRunReadConfig &m_msRunReadConfig,
  SpectrumCollectionHandlerInterface &m_SpectrumCollectionHandlerInterface)
  : m_msRunReadConfig(m_msRunReadConfig),
    m_SpectrumCollectionHandlerInterface(m_SpectrumCollectionHandlerInterface)
{
}

pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::~MzcborSpectrumCollectionReader()
{
}

void
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::setMsRunId(const MsRunIdCstSPtr &msrun_id)
{
  mcsp_msRunId   = msrun_id;
  m_xmlMzMlRunId = mcsp_msRunId.get()->getRunId();
}

void
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::setNativeId2SpectrumIndexMapPtr(
  const std::map<QString, std::size_t> *nativeId2SpectrumIndexMap)
{
  mp_nativeId2SpectrumIndexMap = nativeId2SpectrumIndexMap;
}


void
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::readMsrun()
{
  // qDebug();

  mpa_cborReader->enterContainer();
  bool ms_run_to_read = false;

  while(getExpectedString())
    {
      if(m_expectedString == "id")
        {
          getExpectedString();

          if((m_expectedString == m_xmlMzMlRunId) || (m_xmlMzMlRunId.isEmpty()))
            {
              ms_run_to_read = true;
            }
        }
      else if(m_expectedString == "spectrumList")
        {
          if(ms_run_to_read)
            {
              readSpectrumListAndLeave();
            }
          else
            {
              mpa_cborReader->next();
            }
        }
      else
        {
          mpa_cborReader->next();
        }
    }

  mpa_cborReader->leaveContainer();
}

void
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::readSpectrumListAndLeave()
{
  mpa_cborReader->enterContainer();
  // We want to iterate in the pwiz-spectrum-list and for each pwiz-spectrum
  // create a pappso-spectrum (QualifiedMassSpectrum). Once the pappso mass
  // spectrum has been fully qualified (that is, the member data have been
  // set), it is transferred to the m_SpectrumCollectionHandlerInterface passed as parameter to this
  // function for the consumer to do what it wants with it.

  // Does the m_SpectrumCollectionHandlerInterface consuming the mass spectra read from file want
  // these mass spectra to hold the binary data arrays (mz/i vectors)?

  const bool want_binary_data = m_msRunReadConfig.needPeakList();


  // qDebug();

  // We'll need it to perform the looping in the spectrum list.
  std::size_t spectrum_list_size = 0;

  // qDebug() << "The spectrum list has size:" << spectrum_list_size;

  while(getExpectedString())
    {

      qDebug() << m_expectedString;
      if(m_expectedString == "count")
        {
          if(mpa_cborReader->isUnsignedInteger())
            {
              spectrum_list_size = mpa_cborReader->toUnsignedInteger();
              mpa_cborReader->next();
              qDebug() << "spectrum count=" << spectrum_list_size;
            }

          // Inform the m_SpectrumCollectionHandlerInterface of the spectrum list so that it can
          // handle feedback to the user.
          m_SpectrumCollectionHandlerInterface.spectrumListHasSize(spectrum_list_size);
        }
      else if(m_expectedString == "spectrum")
        {

          mpa_cborReader->enterContainer(); // start array

          std::size_t index_count = 0;
          while(mpa_cborReader->hasNext())
            {
              // If the user of this reader instance wants to stop reading the
              // spectra, then break this loop.
              if(m_SpectrumCollectionHandlerInterface.shouldStop())
                {
                  qDebug() << "The operation was cancelled. Breaking the loop.";
                  m_stopParsing = true;
                  return;
                }

              readSpectrum(index_count, want_binary_data);
              index_count++;
            }

          mpa_cborReader->leaveContainer(); // stop array
        }
      else
        {
          mpa_cborReader->next();
        }
    }


  qDebug() << "Loading ended";
  m_SpectrumCollectionHandlerInterface.loadingEnded();

  mpa_cborReader->leaveContainer();
  m_stopParsing = true;
}


void
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::readSpectrum(std::size_t index_count,
                                                                   bool want_binary_data)
{

  qDebug() << index_count << " " << want_binary_data;
  try
    {
      Spectrum cbor_spectrum;
      cbor_spectrum.fromCbor(*mpa_cborReader, want_binary_data);
      //<spectrum id="controllerType=0 controllerNumber=1 scan=1" index="0"
      // defaultArrayLength="1552">


      // Now, with or without the peak list, we have to craft a qualified mass
      // spectrum that will hold all the metadata about the data potentially
      // found in it.
      QualifiedMassSpectrum qualified_mass_spectrum;

      MassSpectrumId spectrum_id(mcsp_msRunId, cbor_spectrum.index);
      spectrum_id.setNativeId(cbor_spectrum.id);

      spectrum_id.setSpectrumIndex(cbor_spectrum.index);

      qualified_mass_spectrum.setMassSpectrumId(spectrum_id);
      qualified_mass_spectrum.setRtInSeconds(cbor_spectrum.getRtInSeconds());


      if(cbor_spectrum.precursorList.size() > 0)
        {
          qualified_mass_spectrum.setPrecursorNativeId(
            cbor_spectrum.precursorList.at(0).spectrumRef);
          if(mp_nativeId2SpectrumIndexMap != nullptr)
            {
              auto it =
                mp_nativeId2SpectrumIndexMap->find(cbor_spectrum.precursorList.at(0).spectrumRef);
              if(it != mp_nativeId2SpectrumIndexMap->end())
                {
                  qualified_mass_spectrum.setPrecursorSpectrumIndex(it->second);
                }
            }

          for(auto &precursor : cbor_spectrum.precursorList)
            {
              PrecursorIonData ion_data;
              ion_data.mz = precursor.isolationWindow.getTargetMz();
              for(auto &selected_ion : precursor.selectedIonList)
                {
                  ion_data.charge = selected_ion.getChargeState();

                  bool is_ok;
                  ion_data.intensity = selected_ion.getIntensity(&is_ok);
                  ion_data.mz        = selected_ion.getMz();

                  qualified_mass_spectrum.appendPrecursorIonData(ion_data);
                }
            }
        }

      qDebug();
      qualified_mass_spectrum.setMsLevel(cbor_spectrum.getMsLevel());

      qDebug();
      qualified_mass_spectrum.setEmptyMassSpectrum(!cbor_spectrum.defaultArrayLength);

      if(cbor_spectrum.binaryDataArrayList.size() == 2)
        {
          qDebug();
          MassSpectrumSPtr mass_spectrum_sp = std::make_shared<MassSpectrum>();
          qDebug();
          cbor_spectrum.decodeTrace(*(mass_spectrum_sp.get()));
          qDebug();
          qualified_mass_spectrum.setMassSpectrumSPtr(mass_spectrum_sp);
          qDebug();
        }

      qDebug() << "spectrum_id.getNativeId()=" << spectrum_id.getNativeId();
      qDebug() << "rt=" << qualified_mass_spectrum.getRtInSeconds();
      qDebug() << "mslevel=" << qualified_mass_spectrum.getMsLevel();

      if(m_msRunReadConfig.acceptMsLevel(qualified_mass_spectrum.getMsLevel()))
        {
          if(m_msRunReadConfig.acceptRetentionTimeInSeconds(
               qualified_mass_spectrum.getRtInSeconds()))
            {

              if(qualified_mass_spectrum.getMassSpectrumId().getSpectrumIndex() ==
                 std::numeric_limits<std::size_t>::max())
                {
                  throw pappso::PappsoException(
                    QObject::tr("ERROR in %1 spectrum index not initialised").arg(__FUNCTION__));
                }
              m_SpectrumCollectionHandlerInterface.setQualifiedMassSpectrum(
                qualified_mass_spectrum);
            }
        }

      /*
       *
            <cvParam cvRef="MS" accession="MS:1000579" value="" name="MS1 spectrum" />
            <cvParam cvRef="MS" accession="MS:1000130" value="" name="positive scan" />
            <cvParam cvRef="MS" accession="MS:1000285" value="17377980" name="total ion current" />
            <cvParam cvRef="MS" accession="MS:1000127" value="" name="centroid spectrum" />
            <cvParam cvRef="MS" accession="MS:1000504" value="519.138854980469" name="base peak m/z"
       unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
       <cvParam cvRef="MS"
       accession="MS:1000505" value="1702482.375" name="base peak intensity"
       unitAccession="MS:1000131" unitName="number of detector counts" unitCvRef="MS" /> <cvParam
       cvRef="MS" accession="MS:1000528" value="400.153411865234" name="lowest observed m/z"
       unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" /> <cvParam cvRef="MS"
       accession="MS:1000527" value="1013.12335205078" name="highest observed m/z"
       unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" /> <scanList count="1"> <cvParam
       cvRef="MS" accession="MS:1000795" value="" name="no combination" /> <scan
       instrumentConfigurationRef="IC1"> <cvParam cvRef="MS" accession="MS:1000016"
       value="0.0048758833" name="scan start time" unitAccession="UO:0000031" unitName="minute"
       unitCvRef="UO" /> <cvParam cvRef="MS" accession="MS:1000512" value="FTMS + p NSI Full lock ms
       [400.00-1400.00]" name="filter string" /> <cvParam cvRef="MS" accession="MS:1000927"
       value="250" name="ion injection time" unitAccession="UO:0000028" unitName="millisecond"
       unitCvRef="UO" /> <scanWindowList count="1"> <scanWindow> <cvParam cvRef="MS"
       accession="MS:1000501" value="400" name="scan window lower limit" unitAccession="MS:1000040"
       unitName="m/z" unitCvRef="MS" /> <cvParam cvRef="MS" accession="MS:1000500" value="1400"
       name="scan window upper limit" unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
                  </scanWindow>
                </scanWindowList>
              </scan>
            </scanList>
            <binaryDataArrayList count="2">
            */

      // qDebug();
      // Get the native pwiz-spectrum from the spectrum list.
      // Note that this pointer is a shared pointer from pwiz.
      /*
        pwiz::msdata::SpectrumPtr native_pwiz_spectrum_sp =
          getPwizSpectrumPtr(spectrum_list_p.get(), iter, want_binary_data);
        // qDebug();

        // Instantiate the mass spectrum id that will hold critical information
        // like the the native id string and the spectrum index.

        MassSpectrumId massSpectrumId(mcsp_msRunId, iter);

        // qDebug();
        // Get the spectrum native id as a QString to store it in the mass
        // spectrum id class. This is will allow later to refer to the same
        // spectrum starting back from the file.

        QString native_id = QString::fromStdString(native_pwiz_spectrum_sp->id);
        massSpectrumId.setNativeId(native_id);

        // Finally, instantiate the qualified mass spectrum with its id. This
        // function will continue performing pappso-spectrum detailed
        // qualification.

        bool ok = false;

        QualifiedMassSpectrum qualified_mass_spectrum = qualifiedMassSpectrumFromPwizSpectrumPtr(
          massSpectrumId, native_pwiz_spectrum_sp.get(), want_binary_data, ok);

        if(!ok)
          {
            // qDebug() << "Encountered a mass spectrum for which the returned "
            //"status is bad.";
            continue;
          }

        // qDebug();
        // Before handing the mass spectrum out to the m_SpectrumCollectionHandlerInterface, see if
        // the native mass spectrum was empty or not.

        // if(!native_pwiz_spectrum_sp->defaultArrayLength)
        // qDebug() << "The mass spectrum has not defaultArrayLength";

        qualified_mass_spectrum.setEmptyMassSpectrum(!native_pwiz_spectrum_sp->defaultArrayLength);

        // The m_SpectrumCollectionHandlerInterface will receive the index of the mass spectrum in
        the
        // current run via the mass spectrum id member datum.

        // qDebug();
      */
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR reading spectrum index %1 :\%2").arg(index_count).arg(error.qwhat()));
    }
}

std::map<QString, pappso::cbor::mzcbor::CvParam>
pappso::cbor::mzcbor::MzcborSpectrumCollectionReader::readScan()
{
  std::map<QString, CvParam> cv_params;
  mpa_cborReader->enterContainer();
  while(getExpectedString())
    {
      qDebug() << m_expectedString;
      if(m_expectedString == "cvParam")
        {
          cv_params = getCvParamsMap();
        }
      else
        {
          mpa_cborReader->next();
        }
    }
  mpa_cborReader->leaveContainer();
  return cv_params;
}
