/**
 * \file pappsomspp/processing/cbor/psm/evalscan/psmfeaturesscan.cpp
 * \date 15/07/2025
 * \author Olivier Langella
 * \brief compute features 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 "psmfeaturesscan.h"
#include <QCborArray>
#include <QCborMap>
#include "../../../../peptide/peptideproformaparser.h"
#include "../../../../psm/xtandem/xtandemhyperscore.h"


namespace pappso
{
namespace cbor
{
namespace psm
{

PsmFeaturesScan::PsmFeaturesScan(const PsmFileScanProcess &psm_file_scan_process,
                                 pappso::XtandemSpectrumProcess &tandem_spectrum_process,
                                 std::list<Enums::PeptideIon> &ion_list,
                                 pappso::PsmFeatures &psm_features,
                                 pappso::PrecisionPtr fragment_tolerance)
  : CborScanMapBase(psm_file_scan_process),
    m_tandemSpectrumProcess(tandem_spectrum_process),
    m_ionList(ion_list),
    m_psmFeatures(psm_features)
{
  m_fragmentTolerance = fragment_tolerance;
}

PsmFeaturesScan::~PsmFeaturesScan()
{
}

double
PsmFeaturesScan::checkInf(double input) const
{
  if(input < 0)
    return 0;
  return input;
}

void
PsmFeaturesScan::process()
{

  if(keys().contains("psm_list"))
    {
      QualifiedMassSpectrumSPtr qualified_mass_spectrum = getCurrentQualifiedMassSpectrumSPtr();

      pappso::MassSpectrum spectrum =
        m_tandemSpectrumProcess.process(*qualified_mass_spectrum.get()->getMassSpectrumSPtr().get(),
                                        qualified_mass_spectrum.get()->getPrecursorMz(),
                                        qualified_mass_spectrum.get()->getPrecursorCharge());

      // qWarning() << qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();
      QCborArray new_psm_arr;
      for(QCborValue cbor_psm : value("psm_list").toArray())
        {
          QCborMap cbor_psm_map = cbor_psm.toMap();
          QCborMap cbor_psm_features;
          pappso::PeptideSp peptide_sp =
            pappso::PeptideProFormaParser::parseString(cbor_psm_map.value("proforma").toString());

          if(peptide_sp.get()->size() < 2)
            {
              throw pappso::PappsoException(QObject::tr("peptide in psm too small %1")
                                              .arg(cbor_psm_map.value("proforma").toString()));
            }
          // qWarning() << cbor_psm_map.value("proforma").toString() << "end " <<
          // qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();

          std::size_t peptide_size = peptide_sp.get()->size();
          cbor_psm_features.insert(QString("peptide_size"), (unsigned int)peptide_size);

          pappso::XtandemHyperscore hyperscore(spectrum,
                                               peptide_sp,
                                               qualified_mass_spectrum.get()->getPrecursorCharge(),
                                               m_fragmentTolerance,
                                               m_ionList,
                                               true);
          cbor_psm_features.insert(QString("hyperscore"), QCborValue(hyperscore.getHyperscore()));
          // qWarning() << cbor_psm_map.value("proforma").toString() << "end2 " <<
          // qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();

          m_psmFeatures.setPeptideSpectrumCharge(
            peptide_sp,
            qualified_mass_spectrum.get()->getMassSpectrumSPtr().get(),
            qualified_mass_spectrum.get()->getPrecursorCharge(),
            2);
          // TIC
          cbor_psm_features.insert(QString("total_intensity"),
                                   std::log(m_psmFeatures.getTotalIntensity()));
          // MaxIntALL
          cbor_psm_features.insert(QString("max_intensity"),
                                   checkInf(std::log(qualified_mass_spectrum.get()
                                                       ->getMassSpectrumSPtr()
                                                       .get()
                                                       ->maxIntensityDataPoint()
                                                       .y)));

          // qWarning() << cbor_psm_map.value("proforma").toString() << "end3 " <<
          // qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();
          //  MaxYionInt
          cbor_psm_features.insert(
            QString("MaxYionInt"),
            checkInf(std::log(m_psmFeatures.getMaxIntensityPeakIonMatch(Enums::PeptideIon::y))));

          // MaxBionInt
          cbor_psm_features.insert(
            QString("MaxBionInt"),
            checkInf(std::log(m_psmFeatures.getMaxIntensityPeakIonMatch(Enums::PeptideIon::b))));

          // SumYmatchInt
          cbor_psm_features.insert(
            QString("SumYmatchInt"),
            checkInf(std::log(m_psmFeatures.getIntensityOfMatchedIon(Enums::PeptideIon::y))));

          // SumBmatchInt
          cbor_psm_features.insert(
            QString("SumBmatchInt"),
            checkInf(std::log(m_psmFeatures.getIntensityOfMatchedIon(Enums::PeptideIon::b))));

          // FracYmatchInt
          cbor_psm_features.insert(
            QString("FracYmatchInt"),
            checkInf(m_psmFeatures.getIntensityOfMatchedIon(Enums::PeptideIon::y) /
                     m_psmFeatures.getTotalIntensity()));
          // FracBmatchInt
          cbor_psm_features.insert(
            QString("FracBmatchInt"),
            checkInf(m_psmFeatures.getIntensityOfMatchedIon(Enums::PeptideIon::b) /
                     m_psmFeatures.getTotalIntensity()));

          // SeqCoverYion
          cbor_psm_features.insert(
            QString("SeqCoverYion"),
            (double)m_psmFeatures.getAaSequenceCoverage(Enums::PeptideIon::y) /
              (double)peptide_size);
          // SeqCoverBion
          cbor_psm_features.insert(
            QString("SeqCoverBion"),
            (double)m_psmFeatures.getAaSequenceCoverage(Enums::PeptideIon::b) /
              (double)peptide_size);


          // ConsecutiveYion
          cbor_psm_features.insert(
            QString("ConsecutiveYion"),
            (qint64)m_psmFeatures.getMaxConsecutiveIon(Enums::PeptideIon::y));
          // ConsecutiveBion
          cbor_psm_features.insert(
            QString("ConsecutiveBion"),
            (qint64)m_psmFeatures.getMaxConsecutiveIon(Enums::PeptideIon::b));

          // MassErrMean
          cbor_psm_features.insert(QString("MassErrMean"), m_psmFeatures.getMatchedMzDiffMean());

          // MassErrSD
          cbor_psm_features.insert(QString("MassErrSD"), m_psmFeatures.getMatchedMzDiffSd());

          // NumofAnnoPeaks
          cbor_psm_features.insert(QString("NumofAnnoPeaks"),
                                   (unsigned int)m_psmFeatures.getNumberOfMatchedIons());

          // qWarning() << cbor_psm_map.value("proforma").toString() << "end2 " <<
          // qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();
          //  NumofComplementPeaks
          std::size_t num_of_pairs = m_psmFeatures.countMatchedIonComplementPairs();
          cbor_psm_features.insert(QString("NumofComplementPeaks"), (unsigned int)num_of_pairs);
          if(num_of_pairs > 0)
            {
              // SumComplementPeaksInt
              cbor_psm_features.insert(
                QString("SumComplementPeaksInt"),
                std::log(m_psmFeatures.getTotalIntensityOfMatchedIonComplementPairs()));

              // FracComplementPeaksInt
              cbor_psm_features.insert(
                QString("FracComplementPeaksInt"),
                m_psmFeatures.getTotalIntensityOfMatchedIonComplementPairs() /
                  m_psmFeatures.getTotalIntensity());
              // SeqCoverComplementPeaks
              cbor_psm_features.insert(
                QString("SeqCoverComplementPeaks"),
                (double)m_psmFeatures.getComplementPairsAaSequenceCoverage() /
                  (double)peptide_size);
            }
          pappso::LinearRegression lr = m_psmFeatures.getIonIsotopeLinearRegression();
          cbor_psm_features.insert(QString("lrSize"), (unsigned int)lr.getSize());


          double coeff_of_determination = lr.getCoefficientOfDetermination();
          if(std::isnan(coeff_of_determination))
            {
            }
          else
            {
              cbor_psm_features.insert(QString("lrCoeffDet"), coeff_of_determination);
            }


          QCborMap psm_eval = cbor_psm_map.value("eval").toMap();
          psm_eval.remove(QString("features"));
          psm_eval.insert(QString("features"), cbor_psm_features);
          cbor_psm_map.remove(QString("eval"));
          cbor_psm_map.insert(QString("eval"), psm_eval);

          new_psm_arr.push_back(cbor_psm_map);
        }

      insert(QString("psm_list"), new_psm_arr);
      // qWarning() << "end " <<
      // qualified_mass_spectrum.get()->getMassSpectrumId().getSpectrumIndex();
    }
}

} // namespace psm
} // namespace cbor
} // namespace pappso
