/**
 * \file pappsomspp/processing/cbor/psm/psmcborutils.cpp
 * \date 16/09/2025
 * \author Olivier Langella
 * \brief PSM CBOR utilities
 */

/*******************************************************************************
 * 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 <QCborArray>
#include <cstddef>
#include <qtypes.h>

#include "psmcborutils.h"

namespace pappso
{
namespace cbor
{
namespace psm
{
void
PsmCborUtils::prepareCborScanWithSpectrum(
  QCborMap &cbor_scan, const pappso::QualifiedMassSpectrum &ms2_qualified_mass_spectrum)
{
  // id
  QCborMap cbor_scan_id;
  cbor_scan_id.insert(QString("index"),
                      (qint64)ms2_qualified_mass_spectrum.getMassSpectrumId().getSpectrumIndex());
  cbor_scan_id.insert(QString("native_id"),
                      ms2_qualified_mass_spectrum.getMassSpectrumId().getNativeId());
  bool is_ok;
  std::size_t scan =
    ms2_qualified_mass_spectrum.getMassSpectrumId().extractScanNumberFromNativeId(&is_ok);
  if(is_ok)
    {
      cbor_scan_id.insert(QString("scan"), (qint64)scan);
    }
  cbor_scan.insert(QString("id"), cbor_scan_id.toCborValue());


  // precursor
  QCborMap cbor_scan_precursor;
  cbor_scan_precursor.insert(QString("z"), ms2_qualified_mass_spectrum.getPrecursorCharge());
  cbor_scan_precursor.insert(QString("mz"), ms2_qualified_mass_spectrum.getPrecursorMz());
  cbor_scan_precursor.insert(QString("mh"), ms2_qualified_mass_spectrum.getPrecursorMass());
  cbor_scan_precursor.insert(QString("intensity"),
                             ms2_qualified_mass_spectrum.getPrecursorIntensity());
  cbor_scan.insert(QString("precursor"), cbor_scan_precursor.toCborValue());


  // ms2
  QCborMap cbor_scan_ms2;
  cbor_scan_ms2.insert(QString("rt"), ms2_qualified_mass_spectrum.getRtInSeconds());
  cbor_scan.insert(QString("ms2"), cbor_scan_ms2.toCborValue());
}

void
PsmCborUtils::prepareCborScanWithSpectrumAndPeakList(
  QCborMap &cbor_scan, const pappso::QualifiedMassSpectrum &ms2_qualified_mass_spectrum)
{
  prepareCborScanWithSpectrum(cbor_scan, ms2_qualified_mass_spectrum);
  QCborMap spectrum_cbor;

  QCborArray mz_cbor;
  QCborArray intensity_cbor;
  for(const pappso::DataPoint &data_point :
      *(ms2_qualified_mass_spectrum.getMassSpectrumCstSPtr().get()))
    {
      mz_cbor.append(data_point.x);
      intensity_cbor.append(data_point.y);
    }
  spectrum_cbor.insert(QString("mz"), mz_cbor);
  spectrum_cbor.insert(QString("intensity"), intensity_cbor);

  QCborMap new_ms2_map = cbor_scan.value("ms2").toMap();
  new_ms2_map.insert(QString("spectrum"), spectrum_cbor.toCborValue());

  cbor_scan.insert(QString("ms2"), new_ms2_map);
}


std::vector<PsmCborUtils::PsmProteinRef>
PsmCborUtils::getPsmProteinRefList(const QCborMap &cbor_psm)
{
  std::vector<PsmCborUtils::PsmProteinRef> protein_ref_list;

  if(cbor_psm.contains(QString("protein_list")))
    {
      for(auto it : cbor_psm.value("protein_list").toArray())
        {
          QCborMap cbor_protein_ref = it.toMap();
          PsmCborUtils::PsmProteinRef protein_ref;
          protein_ref.accession = cbor_protein_ref.value("accession").toString();

          for(auto ref_position : cbor_protein_ref.value("positions").toArray())
            {
              protein_ref.positions.push_back(ref_position.toInteger());
            }
          protein_ref_list.push_back(protein_ref);
        }
    }

  return protein_ref_list;
}


void
PsmCborUtils::setPsmProteinRefList(QCborMap &cbor_psm,
                                   const std::vector<PsmCborUtils::PsmProteinRef> &protein_ref_list)
{
  QCborArray protein_list;

  for(auto it : protein_ref_list)
    {
      QCborMap protein_ref;
      protein_ref.insert(QString("accession"), it.accession);
      QCborArray positions_arr;
      for(auto position : it.positions)
        {
          positions_arr.append((qint64)position);
        }
      protein_ref.insert(QString("positions"), positions_arr);
      protein_list.append(protein_ref);
    }

  cbor_psm.remove(QString("protein_list"));
  cbor_psm.insert(QString("protein_list"), protein_list);
}


void
PsmCborUtils::mergePsmProteinRefList(QCborMap &cbor_psm_destination,
                                     const QCborMap &cbor_psm_source)
{
  std::vector<PsmCborUtils::PsmProteinRef> protein_ref_list =
    getPsmProteinRefList(cbor_psm_destination);
  std::vector<PsmCborUtils::PsmProteinRef> protein_ref_list_source =
    getPsmProteinRefList(cbor_psm_source);

  protein_ref_list.insert(
    protein_ref_list.end(), protein_ref_list_source.begin(), protein_ref_list_source.end());


  std::sort(protein_ref_list.begin(),
            protein_ref_list.end(),
            [](PsmCborUtils::PsmProteinRef &a, PsmCborUtils::PsmProteinRef &b) {
              return a.accession > b.accession;
            });

  std::vector<PsmCborUtils::PsmProteinRef> unique_protein_ref_list;

  for(auto it = protein_ref_list.begin(); it != protein_ref_list.end(); it++)
    {
      // qDebug() << it->proforma;
      if(unique_protein_ref_list.size() > 0)
        {
          if(unique_protein_ref_list.back().accession == it->accession)
            {
              // merge positions
              unique_protein_ref_list.back().positions.insert(
                unique_protein_ref_list.back().positions.end(),
                it->positions.begin(),
                it->positions.end());

              std::sort(unique_protein_ref_list.back().positions.begin(),
                        unique_protein_ref_list.back().positions.end());

              auto last = std::unique(unique_protein_ref_list.back().positions.begin(),
                                      unique_protein_ref_list.back().positions.end());
              // v now holds {1 2 3 4 5 x x}, where 'x' is indeterminate
              unique_protein_ref_list.back().positions.erase(
                last, unique_protein_ref_list.back().positions.end());
            }
          else
            {
              unique_protein_ref_list.push_back(*it);
            }
        }
      else
        {
          unique_protein_ref_list.push_back(*it);
        }

      qDebug();
    }

  setPsmProteinRefList(cbor_psm_destination, unique_protein_ref_list);
}


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