/**
 * \file core/peptidoms.cpp
 * \date 12/09/2025
 * \author Olivier Langella
 * \brief Running peptidoms
 */


/*
 * PeptidOMS, Spectra to protein alignment tool
 * Copyright (C) 2025  Olivier Langella
 * <olivier.langella@universite-paris-saclay.fr>
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "runningpeptidoms.h"
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/amino_acid/aamodification.h>
#include <pappsomspp/core/fasta/fastareader.h>
#include <pappsomspp/core/processing/cbor/psm/psmcborutils.h>
#include <QtConcurrent>

RunningPeptidOms::RunningPeptidOms(pappso::UiMonitorInterface &monitor,
                                   const QJsonObject &json_params)
  : m_monitor(monitor)
{
  m_aaCode.addAaModification('C',
                             pappso::AaModification::getInstance("MOD:00397"));


  m_peptidOmsParameters = json_params;

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

  QJsonObject spectrum_param = json_params.value("spectrum").toObject();

  m_minimumMz    = spectrum_param.value("minimum_mz").toDouble();
  m_nMostIntense = spectrum_param.value("n_most_intense").toInt();
  m_deisotope    = spectrum_param.value("deisotope").toBool();
  m_maxInterpretationsPerSpectrum =
    json_params.value("max_interpretations_per_spectrum").toInt();
  qDebug() << "m_maxInterpretationsPerSpectrum="
           << m_maxInterpretationsPerSpectrum;
}

RunningPeptidOms::~RunningPeptidOms()
{
  if(mpa_peptidOmsCborOutput != nullptr)
    delete mpa_peptidOmsCborOutput;
}

void
RunningPeptidOms::loadProteinList(const QFileInfo &fasta_file_info)
{
  FastaSeqHandler seq_handler(this);
  pappso::FastaReader fasta_reader(seq_handler);
  QFile fasta(fasta_file_info.absoluteFilePath());
  fasta_reader.parse(fasta);
}

void
RunningPeptidOms::SpectrumCollectionHandler::setQualifiedMassSpectrum(
  const pappso::QualifiedMassSpectrum &spectrum)
{

  mp_parent->m_scanResultArray.push_back(
    new OneScanProcess(mp_parent, spectrum.makeQualifiedMassSpectrumCstSPtr()));

  if(mp_parent->m_scanResultArray.size() > 1000)
    {
      mp_parent->processScanResultArray();
    }
}

void
RunningPeptidOms::onOneScanProcessStarted(std::size_t spectrum_index)
{
  QMutexLocker locker(&m_mutex);
  m_runningSpectrumStack.push_back(spectrum_index);
  m_monitor.setStatus(
    QString("starting process for spectrum index %1 (total done: %2)")
      .arg(spectrum_index)
      .arg(m_countScanFinished));
}

void
RunningPeptidOms::onOneScanProcessFinished(std::size_t spectrum_index)
{
  QMutexLocker locker(&m_mutex);

  auto it = std::find(m_runningSpectrumStack.begin(),
                      m_runningSpectrumStack.end(),
                      spectrum_index);

  if(it != m_runningSpectrumStack.end())
    {
      m_runningSpectrumStack.erase(it);
    }
  if(m_runningSpectrumStack.size() > 0)
    {
      m_monitor.setStatus(
        QString("Process for spectrum index %1 finished. Slowest one is %2")
          .arg(spectrum_index)
          .arg(m_runningSpectrumStack.front()));
    }

  m_countScanFinished++;
}


void
RunningPeptidOms::processScanResultArray()
{

  qDebug();
  m_monitor.setStatus(QString("spectrum id %1")
                        .arg(m_scanResultArray.front()
                               ->getQualifiedMassSpectrum()
                               .getMassSpectrumId()
                               .getSpectrumIndex()));

  qDebug();
  std::function<void(OneScanProcess *)> mapProcessScanResult =
    [](OneScanProcess *scan_result) { scan_result->process(); };

  m_runningSpectrumStack.clear();
  QFuture<void> res =
    QtConcurrent::map<std::vector<OneScanProcess *>::iterator>(
      m_scanResultArray.begin(), m_scanResultArray.end(), mapProcessScanResult);
  res.waitForFinished();

  qDebug();
  for(auto &scan : m_scanResultArray)
    {
      qDebug() << "spectrum "
               << scan->getQualifiedMassSpectrum()
                    .getMassSpectrumId()
                    .getSpectrumIndex()
               << " OK";
      scan->writeCborStream(mpa_peptidOmsCborOutput->getScanCborWriter(),
                            m_maxInterpretationsPerSpectrum);
      scan->fillProteinMap(mpa_peptidOmsCborOutput->getPsmProteinMap());

      delete scan;
    }
  m_scanResultArray.clear();

  /*
   * /home/langella/developpement/git/peptidoms/specglobtoolcpp/src/core/runningpeptidoms.cpp@186, RunningPeptidOms::ScanResult::process():
/home/langella/developpement/git/peptidoms/specglobtoolcpp/src/core/runningpeptidoms.cpp@192,
RunningPeptidOms::ScanResult::process():
/home/langella/developpement/git/peptidoms/specglobtoolcpp/src/core/runningpeptidoms.cpp@201,
RunningPeptidOms::ScanResult::process():
/home/langella/developpement/git/peptidoms/specglobtoolcpp/src/core/runningpeptidoms.cpp@207,
RunningPeptidOms::ScanResult::process(): Best post-processed alignment
"[168.09]EEEK[30.0462]KEE[150.078]" 17 SPC 10
/home/langella/developpement/git/peptidoms/specglobtoolcpp/src/core/runningpeptidoms.cpp@156,
RunningPeptidOms::ScanResult::process(): beginning= 1 length= 35 tree= 71 score=
18 protein= "GRMZM2G123558_P01"  spectrum_index= 2037 Erreur de segmentation
(core dumped)
*/
  qDebug();
}


void
RunningPeptidOms::run(pappso::MsRunReaderSPtr msrun_reader,
                      const std::vector<QFileInfo> &fasta_fileinfo_list,
                      pappso::cbor::CborStreamWriter *p_cbor_stream_writer)
{
  m_monitor.setStatus("starting PeptidOMS");

  m_monitor.setStatus("loading protein list");
  for(const QFileInfo &file_info : fasta_fileinfo_list)
    {
      loadProteinList(file_info);
    }
  mpa_peptidOmsCborOutput = new PeptidOmsCborOutput(
    p_cbor_stream_writer, m_peptidOmsParameters, "peptidoms");

  mpa_peptidOmsCborOutput->setTargetFastaFiles(fasta_fileinfo_list);


  mpa_peptidOmsCborOutput->setMsRunReaderSPtr(msrun_reader);

  pappso::MsRunReadConfig config;
  config.setMsLevels({2});
  config.setNeedPeakList(true);
  SpectrumCollectionHandler spectrum_collection_handler(this, m_monitor);
  msrun_reader.get()->readSpectrumCollection2(config,
                                              spectrum_collection_handler);


  processScanResultArray();
  mpa_peptidOmsCborOutput->close();
}
