/**
 * \file pappsomspp/processing/cbor/psm/cborscanmapbase.h
 * \date 13/07/2025
 * \author Olivier Langella
 * \brief Base class to handle scan cbor map
 */

/*******************************************************************************
 * 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 "cborscanmapbase.h"
#include "pappsomspp/core/pappsoexception.h"
#include "psmfilescanprocess.h"
#include "psmcborutils.h"
#include <QCborArray>


namespace pappso
{
namespace cbor
{

psm::CborScanMapBase::CborScanMapBase(const PsmFileScanProcess &psm_file_scan_process)
  : m_PsmFileScanProcess(psm_file_scan_process)
{
}

psm::CborScanMapBase::~CborScanMapBase()
{
}

void
psm::CborScanMapBase::process()
{
}

QCborMap
pappso::cbor::psm::CborScanMapBase::getCborScanId() const
{
  if(!keys().contains("id"))
    {
      throw pappso::PappsoException(QObject::tr("missing id in scan"));
    }
  return value("id").toMap();
}

QCborMap
pappso::cbor::psm::CborScanMapBase::getCborScanPrecursor() const
{
  if(!keys().contains("precursor"))
    {
      throw pappso::PappsoException(QObject::tr("missing precursor in scan"));
    }
  return value("precursor").toMap();
}

QCborArray
pappso::cbor::psm::CborScanMapBase::getCborPsmList() const
{
  if(!keys().contains("psm_list"))
    {
      throw pappso::PappsoException(QObject::tr("missing psm_list in scan"));
    }
  return value("psm_list").toArray();
}


std::vector<double>::iterator
psm::CborScanMapBase::addPsmEvalVectorDouble(const QString &eval_name,
                                             const QString &eval_value_key,
                                             std::vector<double>::iterator itbegin,
                                             std::vector<double>::const_iterator itend)
{
  std::vector<double>::iterator it = itbegin;
  if(keys().contains("psm_list"))
    {


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

          if(!cbor_psm_map.keys().contains("proforma"))
            {
              throw pappso::PappsoException(
                QObject::tr("missing proforma in psm %1").arg(cbor_psm_map.keys().size()));
            }
          QCborMap cbor_psm_eval_newvalues;


          if(it == itend)
            {
              throw pappso::PappsoException(
                QObject::tr("no more values to add in psm %1").arg(cbor_psm_map.keys().size()));
            }
          cbor_psm_eval_newvalues.insert(eval_value_key, *it);
          it++;


          QCborMap psm_eval = cbor_psm_map.value("eval").toMap();
          psm_eval.remove(eval_name);
          psm_eval.insert(eval_name, cbor_psm_eval_newvalues);
          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);
      remove(QString("psm_list"));
      insert(QString("psm_list"), new_psm_arr);
    }
  return it;
}


pappso::QualifiedMassSpectrumSPtr
psm::CborScanMapBase::getCurrentQualifiedMassSpectrumSPtr() const
{
  if(isEmpty())
    {
      throw pappso::PappsoException(QObject::tr("ERROR: cbor scan map is empty"));
    }
  if(!contains(QString("precursor")))
    {
      throw pappso::PappsoException(QObject::tr("ERROR: no precursor in scan"));
    }
  const QCborMap cbor_precursor(value("precursor").toMap());
  if(!contains(QString("ms2")))
    {
      throw pappso::PappsoException(QObject::tr("ERROR: ms2 is empty in scan"));
    }
  const QCborMap cbor_ms2(value("ms2").toMap());

  if(!contains(QString("id")))
    {
      throw pappso::PappsoException(QObject::tr("ERROR: id is empty in scan"));
    }
  const QCborMap cbor_id(value("id").toMap());

  if(!cbor_id.keys().contains("index"))
    {
      throw pappso::PappsoException("There is no scan index");
    }

  if(!cbor_ms2.keys().contains("spectrum"))
    {
      throw pappso::PappsoException("There is no spectrum in ms2");
    }

  const QCborMap cbor_spectrum(cbor_ms2.value("spectrum").toMap());

  if(!cbor_spectrum.keys().contains("mz"))
    {
      throw pappso::PappsoException("There is no cbor_spectrum mz values");
    }
  if(!cbor_spectrum.keys().contains("intensity"))
    {
      throw pappso::PappsoException("There is no cbor_spectrum intensity values");
    }
  pappso::MsRunId msrun_id(m_PsmFileScanProcess.m_currentPeaklistFile.name);
  msrun_id.setSampleName(m_PsmFileScanProcess.m_currentSampleName);
  pappso::MsRunIdCstSPtr msrun_id_sp = std::make_shared<const pappso::MsRunId>(msrun_id);
  pappso::MassSpectrumId ms_id(msrun_id_sp);
  ms_id.setSpectrumIndex(cbor_id.value("index").toInteger());

  // native_id
  if(cbor_id.keys().contains("native_id"))
    {
      ms_id.setNativeId(cbor_id.value("native_id").toString());
    }

  std::vector<DataPoint> data_point_vector;
  std::size_t i = 0;
  for(auto cbor_mz_value : cbor_spectrum.value("mz").toArray())
    {
      data_point_vector.push_back(
        {cbor_mz_value.toDouble(), cbor_spectrum.value("intensity").toArray().at(i).toDouble()});
      i++;
    }


  MassSpectrum mass_spectrum(data_point_vector);
  pappso::PrecursorIonData precursor_ion_data;

  pappso::QualifiedMassSpectrum qualified_mass_spectrum(ms_id);
  qualified_mass_spectrum.setMassSpectrumSPtr(mass_spectrum.makeMassSpectrumSPtr());
  qualified_mass_spectrum.setMsLevel(2);

  if(cbor_precursor.keys().contains("z"))
    {
      precursor_ion_data.charge = cbor_precursor.value("z").toInteger();
    }
  if(cbor_precursor.keys().contains("mz"))
    {
      precursor_ion_data.mz = cbor_precursor.value("mz").toDouble();
    }
  if(cbor_precursor.keys().contains("intensity"))
    {
      precursor_ion_data.intensity = cbor_precursor.value("intensity").toDouble();
    }
  qualified_mass_spectrum.appendPrecursorIonData(precursor_ion_data);
  if(cbor_ms2.keys().contains("rt"))
    {
      qualified_mass_spectrum.setRtInSeconds(cbor_ms2.value("rt").toDouble());
    }


  return qualified_mass_spectrum.makeQualifiedMassSpectrumSPtr();
}
void
psm::CborScanMapBase::populateProteinMapUsingOldProteinMap(const PsmProteinMap &old_protein_map,
                                                           PsmProteinMap &new_protein_map) const
{
  if(isEmpty())
    {
      throw pappso::PappsoException(QObject::tr("ERROR: cbor scan map is empty"));
    }
  if(!contains(QString("psm_list")))
    {
      throw pappso::PappsoException(QObject::tr("ERROR: no psm_list in scan"));
    }

  for(auto it_psm : value("psm_list").toArray())
    {
      QCborMap psm = it_psm.toMap();
      if(!psm.contains(QString("protein_list")))
        {
          throw pappso::PappsoException(
            QObject::tr("ERROR: no protein_list in psm, psm_list in scan"));
        }

      for(auto it_protein : psm.value("protein_list").toArray())
        {
          new_protein_map.insert(
            old_protein_map.getByAccession(it_protein.toMap().value("accession").toString()));
        }
    }
}


void
psm::CborScanMapBase::filterAndSortPsmList()
{
  // cbor_writer.writeCborMap(*this);
}


void
psm::CborScanMapBase::filterPsmListUniqueUniqueProforma()
{


  struct uniquePsmResult
  {
    QString proforma;
    QCborMap psm;
  };

  QCborArray old_psm_arr = value("psm_list").toArray();
  QCborArray new_psm_arr;
  // for(QCborValue cbor_psm : old_psm_arr) {


  std::vector<uniquePsmResult> all_psm_list;
  for(auto it_psm : old_psm_arr)
    {
      QCborMap psm_map = it_psm.toMap();
      all_psm_list.push_back({psm_map.value("proforma").toString(), psm_map});
    }
  qDebug();
  std::sort(all_psm_list.begin(), all_psm_list.end(), [](uniquePsmResult &a, uniquePsmResult &b) {
    return a.proforma > b.proforma;
  });

  qDebug();
  std::vector<uniquePsmResult> unique_psm_list;

  for(auto it = all_psm_list.begin(); it != all_psm_list.end(); it++)
    {
      qDebug() << it->proforma;
      if(unique_psm_list.size() > 0)
        {
          if(unique_psm_list.back().proforma == it->proforma)
            {
              // merge ?
              PsmCborUtils::mergePsmProteinRefList(unique_psm_list.back().psm, it->psm);
            }
          else
            {
              unique_psm_list.push_back(*it);
            }
        }
      else
        {
          unique_psm_list.push_back(*it);
        }

      qDebug();
    }


  qDebug() << unique_psm_list.size();
  for(auto it = unique_psm_list.begin(); it != unique_psm_list.end(); it++)
    {
      new_psm_arr.append(it->psm);
      qDebug();
    }

  // qWarning() << new_psm_arr.size();
  remove(QString("psm_list"));
  insert(QString("psm_list"), new_psm_arr);
}

} // namespace cbor
} // namespace pappso
