/**
 * \file pappsomspp/processing/cbor/psm/evalscan/psmspecglob.cpp
 * \date 19/07/2025
 * \author Olivier Langella
 * \brief compute specglob alignment on scan's PSM
 */

/*******************************************************************************
 * 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 "psmionseries.h"
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/utils.h"
#include "pappsomspp/core/peptide/peptideproformaparser.h"
#include "pappsomspp/core/psm/peptideisotopespectrummatch.h"
#include <QCborArray>

pappso::cbor::psm::PsmIonSeries::PsmIonSeries(std::size_t buffer_scan_size,
                                              pappso::cbor::CborStreamWriter *cbor_output_p,
                                              const QJsonObject &parameters)
  : PsmFileScanProcessAndCopy(buffer_scan_size, cbor_output_p, "IonSeries")
{
  m_psmIonSeriesParameters = parameters;

  if(parameters.value("fragment_tolerance_unit").toString() == "dalton")
    {
      m_fragmentTolerance = pappso::PrecisionFactory::getDaltonInstance(
        parameters.value("fragment_tolerance").toDouble());
    }
  else if(parameters.value("fragment_tolerance_unit").toString() == "ppm")
    {
      m_fragmentTolerance =
        pappso::PrecisionFactory::getPpmInstance(parameters.value("fragment_tolerance").toDouble());
    }
}

pappso::cbor::psm::PsmIonSeries::~PsmIonSeries()
{
}

void
pappso::cbor::psm::PsmIonSeries::parameterMapReady(pappso::UiMonitorInterface &monitor
                                                   [[maybe_unused]])
{
  QCborMap cbor_features_parameters;

  cbor_features_parameters.insert(QString("fragment_tolerance_unit"),
                                  Utils::toString(m_fragmentTolerance->unit()));
  cbor_features_parameters.insert(QString("fragment_tolerance"), m_fragmentTolerance->getNominal());
  cbor_features_parameters.insert(QString("ion_list"), "y b");


  m_cborParameterMap.insert(QString("ion-series"), cbor_features_parameters);

  mp_cborOutput->append("parameter_map");
  mp_cborOutput->writeCborMap(m_cborParameterMap);
}


pappso::cbor::psm::CborScanMapBase *
pappso::cbor::psm::PsmIonSeries::newCborScanMap()
{
  return new PsmIonSeriesScan(*this, m_fragmentTolerance);
}


pappso::cbor::psm::PsmIonSeriesScan::PsmIonSeriesScan(
  const pappso::cbor::psm::PsmIonSeries &psm_specpeptidoms, pappso::PrecisionPtr fragment_tolerance)
  : CborScanMapBase(psm_specpeptidoms)
{
  m_fragmentTolerance = fragment_tolerance;
  mp_psmSpecPeptidOms = &psm_specpeptidoms;
}

pappso::cbor::psm::PsmIonSeriesScan::~PsmIonSeriesScan()
{
}

void
pappso::cbor::psm::PsmIonSeriesScan::process()
{
  // qWarning() <<"PsmIonSeriesScan::process" << keys();
  if(!keys().contains("id"))
    {
      throw pappso::PappsoException(QObject::tr("missing scan id"));
    }
  if(keys().contains("psm_list"))
    {
      QualifiedMassSpectrumSPtr qualified_mass_spectrum = getCurrentQualifiedMassSpectrumSPtr();

      int precursor_charge = qualified_mass_spectrum.get()->getPrecursorCharge();

      QCborArray new_psm_arr;
      for(QCborValue cbor_psm : value("psm_list").toArray())
        {
          QCborMap cbor_psm_map = cbor_psm.toMap();

          qDebug() << "PsmIonSeriesScan::process cbor_psm_map.keys() " << cbor_psm_map.keys();
          if(!cbor_psm_map.keys().contains("proforma"))
            {
              throw pappso::PappsoException(
                QObject::tr("missing proforma in psm %1").arg(cbor_psm_map.keys().size()));
            }
          pappso::PeptideSp peptide_sp =
            pappso::PeptideProFormaParser::parseString(cbor_psm_map.value("proforma").toString());
          qDebug() << peptide_sp.get()->toString();
          pappso::PeptideIsotopeSpectrumMatch psm(
            *(qualified_mass_spectrum.get()->getMassSpectrumSPtr().get()),
            peptide_sp,
            precursor_charge,
            m_fragmentTolerance,
            pappso::PeptideFragmentIonListBase::getCIDionList(),
            2,
            0);

          struct data_plot
          {
            pappso::DataPoint point;
            double mz_th;
            int charge;
            int size;
            int isotope_number;
            int isotope_rank;
          };
          std::map<pappso::Enums::PeptideIon, std::vector<data_plot>> ion_map;


          for(const pappso::PeakIonIsotopeMatch &peakion_match : psm)
            {
              data_plot one_ion_match;
              one_ion_match.charge = (int)peakion_match.getCharge();
              one_ion_match.isotope_number =
                (int)peakion_match.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeNumber();
              one_ion_match.isotope_rank =
                (int)peakion_match.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeRank();
              one_ion_match.mz_th =
                peakion_match.getPeptideNaturalIsotopeAverageSp().get()->getMz();
              one_ion_match.point = peakion_match.getPeak();
              one_ion_match.size  = (int)peakion_match.getPeptideFragmentIonSp().get()->size();

              auto it = ion_map.insert({peakion_match.getPeptideIonType(), {}});
              it.first->second.push_back(one_ion_match);
            }

          QCborMap cbor_psm_props_peptide;
          cbor_psm_props_peptide.insert(QString("mz"), peptide_sp.get()->getMz(precursor_charge));
          cbor_psm_props_peptide.insert(QString("mh"), peptide_sp.get()->getMz(1));

          QCborMap cbor_psm_props_ion_series;
          for(auto &cle_valeur : ion_map)
            {
              std::vector<data_plot> &current_vector = cle_valeur.second;

              std::sort(
                current_vector.begin(),
                current_vector.end(),
                [](const data_plot &a, const data_plot &b) { return a.point.x < b.point.x; });
              QCborArray current_ion_json_arr;

              for(const data_plot &one_point : current_vector)
                {
                  QCborMap current_ion;
                  current_ion.insert(QString("mz"), one_point.point.x);
                  current_ion.insert(QString("mzth"), one_point.mz_th);
                  current_ion.insert(QString("intensity"), one_point.point.y);
                  current_ion.insert(QString("size"), one_point.size);
                  current_ion.insert(QString("charge"), one_point.charge);
                  current_ion.insert(QString("isotope"), one_point.isotope_number);
                  current_ion.insert(QString("rank"), one_point.isotope_rank);

                  current_ion_json_arr.push_back(current_ion);
                }

              cbor_psm_props_ion_series.insert(pappso::Utils::toString(cle_valeur.first),
                                               current_ion_json_arr);
            }


          QCborMap cbor_psm_props;
          if(cbor_psm_map.keys().contains("props"))
            {
              cbor_psm_props = cbor_psm_map.value("props").toMap();
            }
          cbor_psm_props.remove(QString("peptide"));
          cbor_psm_props.insert(QString("peptide"), cbor_psm_props_peptide);

          cbor_psm_props.remove(QString("ion-series"));
          cbor_psm_props.insert(QString("ion-series"), cbor_psm_props_ion_series);
          cbor_psm_map.remove(QString("props"));
          cbor_psm_map.insert(QString("props"), cbor_psm_props);

          QCborValue copy_eval = cbor_psm_map.value("eval");
          cbor_psm_map.remove(QString("eval"));
          cbor_psm_map.insert(QString("eval"), copy_eval);

          qDebug() << "PsmIonSeriesScan::process cbor_psm_map.keys() " << cbor_psm_map.keys();
          new_psm_arr.push_back(cbor_psm_map);
        }

      // insert(QString("psm_list"), new_psm_arr);
      // remove(QString("psm_list"));
      insert(QString("psm_list"), new_psm_arr);
    }
}
