/**
 * \file input/sage/sagereader.h
 * \date 21/08/2024
 * \author Olivier Langella
 * \brief read data files from Sage output
 */

/*******************************************************************************
 * Copyright (c) 2024 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of i2MassChroQ.
 *
 *     i2MassChroQ 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.
 *
 *     i2MassChroQ 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 i2MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/


#include "sagetsvhandler.h"
#include "pappsomspp/core/exception/exceptionnotimplemented.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"
#include "pappsomspp/core/exception/exceptionoutofrange.h"
#include "pappsomspp/core/exception/exceptioninterrupted.h"
#include "../../../../peptide/peptideproformaparser.h"
#include "pappsomspp/core/types.h"
#include <qcbormap.h>
#include <qregularexpression.h>


namespace pappso
{
namespace cbor
{
namespace psm
{

SageTsvHandler::SageTsvHandler(pappso::UiMonitorInterface *p_monitor,
                               const SageReader &sage_reader,
                               PsmProteinMap &psm_protein_map)
  : m_sageReader(sage_reader), m_psmProteinMap(psm_protein_map)
{
  mp_monitor                 = p_monitor;
  m_staticModificationList   = sage_reader.getStaticModificationList();
  m_variableModificationList = sage_reader.getVariableModificationList();
  m_decoyTag                 = sage_reader.getDecoyTag();
}

SageTsvHandler::~SageTsvHandler()
{
}

void
SageTsvHandler::writeSampleList()
{
  for(auto it_sample_map : m_sampleMap)
    {
      writeSample(it_sample_map.second);
    }
}

void
SageTsvHandler::endLine()
{
  if(m_line.charge != 0)
    recordLine();
  m_lineNumber++;
}

void
SageTsvHandler::endSheet()
{
}

void
SageTsvHandler::setCell(const OdsCell &cell)
{
  if(m_lineNumber == 0)
    {
      // header
      QString tag = cell.getStringValue();
      if(tag == "psm_id")
        {
          m_columnTypeList.push_back(Columns::psm_id);
        }
      else if(tag == "peptide")
        {
          m_columnTypeList.push_back(Columns::peptide);
        }
      else if(tag == "proteins")
        {
          m_columnTypeList.push_back(Columns::proteins);
        }
      else if(tag == "protein_groups")
        {
          m_columnTypeList.push_back(Columns::protein_groups);
        }
      else if(tag == "num_protein_groups")
        {
          m_columnTypeList.push_back(Columns::num_protein_groups);
        }
      else if(tag == "num_proteins")
        {
          m_columnTypeList.push_back(Columns::num_proteins);
        }
      else if(tag == "filename")
        {
          m_columnTypeList.push_back(Columns::filename);
        }
      else if(tag == "scannr")
        {
          m_columnTypeList.push_back(Columns::scannr);
        }
      else if(tag == "rank")
        {
          m_columnTypeList.push_back(Columns::rank);
        }
      else if(tag == "label")
        {
          m_columnTypeList.push_back(Columns::label);
        }
      else if(tag == "expmass")
        {
          m_columnTypeList.push_back(Columns::expmass);
        }

      else if(tag == "calcmass")
        {
          m_columnTypeList.push_back(Columns::calcmass);
        }
      else if(tag == "charge")
        {
          m_columnTypeList.push_back(Columns::charge);
        }
      else if(tag == "peptide_len")
        {
          m_columnTypeList.push_back(Columns::peptide_len);
        }
      else if(tag == "missed_cleavages")
        {
          m_columnTypeList.push_back(Columns::missed_cleavages);
        }
      else if(tag == "semi_enzymatic")
        {
          m_columnTypeList.push_back(Columns::semi_enzymatic);
        }
      else if(tag == "isotope_error")
        {
          m_columnTypeList.push_back(Columns::isotope_error);
        }
      else if(tag == "precursor_ppm")
        {
          m_columnTypeList.push_back(Columns::precursor_ppm);
        }
      else if(tag == "fragment_ppm")
        {
          m_columnTypeList.push_back(Columns::fragment_ppm);
        }
      else if(tag == "hyperscore")
        {
          m_columnTypeList.push_back(Columns::hyperscore);
        }
      else if(tag == "delta_next")
        {
          m_columnTypeList.push_back(Columns::delta_next);
        }
      else if(tag == "delta_best")
        {
          m_columnTypeList.push_back(Columns::delta_best);
        }
      else if(tag == "rt")
        {
          m_columnTypeList.push_back(Columns::rt);
        }
      else if(tag == "aligned_rt")
        {
          m_columnTypeList.push_back(Columns::aligned_rt);
        }
      else if(tag == "predicted_rt")
        {
          m_columnTypeList.push_back(Columns::predicted_rt);
        }
      else if(tag == "delta_rt_model")
        {
          m_columnTypeList.push_back(Columns::delta_rt_model);
        }
      else if(tag == "ion_mobility")
        {
          m_columnTypeList.push_back(Columns::ion_mobility);
        }
      else if(tag == "predicted_mobility")
        {
          m_columnTypeList.push_back(Columns::predicted_mobility);
        }
      else if(tag == "delta_mobility")
        {
          m_columnTypeList.push_back(Columns::delta_mobility);
        }
      else if(tag == "matched_peaks")
        {
          m_columnTypeList.push_back(Columns::matched_peaks);
        }
      else if(tag == "longest_b")
        {
          m_columnTypeList.push_back(Columns::longest_b);
        }
      else if(tag == "longest_y")
        {
          m_columnTypeList.push_back(Columns::longest_y);
        }
      else if(tag == "longest_y_pct")
        {
          m_columnTypeList.push_back(Columns::longest_y_pct);
        }
      else if(tag == "matched_intensity_pct")
        {
          m_columnTypeList.push_back(Columns::matched_intensity_pct);
        }
      else if(tag == "scored_candidates")
        {
          m_columnTypeList.push_back(Columns::scored_candidates);
        }
      else if(tag == "poisson")
        {
          m_columnTypeList.push_back(Columns::poisson);
        }
      else if(tag == "sage_discriminant_score")
        {
          m_columnTypeList.push_back(Columns::sage_discriminant_score);
        }
      else if(tag == "posterior_error")
        {
          m_columnTypeList.push_back(Columns::posterior_error);
        }
      else if(tag == "spectrum_q")
        {
          m_columnTypeList.push_back(Columns::spectrum_q);
        }
      else if(tag == "peptide_q")
        {
          m_columnTypeList.push_back(Columns::peptide_q);
        }
      else if(tag == "protein_q")
        {
          m_columnTypeList.push_back(Columns::protein_q);
        }
      else if(tag == "protein_group_q")
        {
          m_columnTypeList.push_back(Columns::protein_group_q);
        }
      else if(tag == "ms2_intensity")
        {
          m_columnTypeList.push_back(Columns::ms2_intensity);
        }
      else
        {
          throw pappso::ExceptionNotPossible(QObject::tr("column \"%1\" not defined").arg(tag));
        }
    }
  else
    {
      if(m_columnNumber >= m_columnTypeList.size())
        {
          throw pappso::ExceptionOutOfRange(
            QObject::tr("the value %1 is out of range").arg(cell.getStringValue()));
        }
      Columns column_type = m_columnTypeList[m_columnNumber];
      switch(column_type)
        {
          case Columns::psm_id:
            break;
          case Columns::protein_groups:
            break;
          case Columns::num_protein_groups:
            break;
          case Columns::protein_group_q:
            break;
          case Columns::peptide:
            parsePeptide(cell.toString());
            break;
          case Columns::proteins:
            parseProteins(cell.toString());
            break;
          case Columns::num_proteins:
            if((std::size_t)cell.getDoubleValue() != (std::size_t)m_proteinList.size())
              {
                throw pappso::PappsoException(
                  QObject::tr("column \"num_proteins\"!=%1").arg(m_proteinList.size()));
              }
            break;
          case Columns::filename:
            parseMsRunFilename(cell.toString());
            break;
          case Columns::scannr:
            parseScanNrColumn(cell.toString());
            break;
          case Columns::rank:
            m_line.rank = cell.getDoubleValue();
            break;
          case Columns::label:
            m_line.label = cell.getDoubleValue();
            break;
          case Columns::expmass:
            m_line.expmass = cell.getDoubleValue();
            break;
          case Columns::calcmass:
            m_line.calcmass = cell.getDoubleValue();
            break;
          case Columns::charge:
            m_line.charge = cell.getDoubleValue();
            break;
          case Columns::peptide_len:
            m_line.peptide_len = cell.getDoubleValue();
            break;
          case Columns::missed_cleavages:
            m_line.missed_cleavages = cell.getDoubleValue();
            break;
          case Columns::semi_enzymatic:
            m_line.semi_enzymatic = cell.getDoubleValue();
            break;
          case Columns::isotope_error:
            m_line.isotope_error = cell.getDoubleValue();
            break;

          case Columns::precursor_ppm:
            m_line.precursor_ppm = cell.getDoubleValue();
            break;
          case Columns::fragment_ppm:
            m_line.fragment_ppm = cell.getDoubleValue();
            break;
          case Columns::hyperscore:
            m_line.hyperscore = cell.getDoubleValue();
            break;
          case Columns::delta_next:
            m_line.delta_next = cell.getDoubleValue();
            break;
          case Columns::delta_best:
            m_line.delta_best = cell.getDoubleValue();
            break;
          case Columns::rt:
            m_line.rt = cell.getDoubleValue() * 60; // to convert retention time in seconds
            break;
          case Columns::aligned_rt:
            m_line.aligned_rt = cell.getDoubleValue();
            break;
          case Columns::predicted_rt:
            m_line.predicted_rt = cell.getDoubleValue();
            break;
          case Columns::delta_rt_model:
            m_line.delta_rt_model = cell.getDoubleValue();
            break;
          case Columns::ion_mobility:
            m_line.ion_mobility = cell.getDoubleValue();
            break;
          case Columns::predicted_mobility:
            m_line.predicted_mobility = cell.getDoubleValue();
            break;
          case Columns::delta_mobility:
            m_line.delta_mobility = cell.getDoubleValue();
            break;
          case Columns::matched_peaks:
            m_line.matched_peaks = cell.getDoubleValue();
            break;
          case Columns::longest_b:
            m_line.longest_b = cell.getDoubleValue();
            break;
          case Columns::longest_y:
            m_line.longest_y = cell.getDoubleValue();
            break;
          case Columns::longest_y_pct:
            m_line.longest_y_pct = cell.getDoubleValue();
            break;
          case Columns::matched_intensity_pct:
            m_line.matched_intensity_pct = cell.getDoubleValue();
            break;
          case Columns::scored_candidates:
            m_line.scored_candidates = cell.getDoubleValue();
            break;
          case Columns::poisson:
            m_line.poisson = cell.getDoubleValue();
            break;
          case Columns::sage_discriminant_score:
            m_line.sage_discriminant_score = cell.getDoubleValue();
            break;
          case Columns::posterior_error:
            m_line.posterior_error = cell.getDoubleValue();
            break;
          case Columns::spectrum_q:
            m_line.spectrum_q = cell.getDoubleValue();
            break;
          case Columns::peptide_q:
            m_line.peptide_q = cell.getDoubleValue();
            break;
          case Columns::protein_q:
            m_line.protein_q = cell.getDoubleValue();
            break;
          case Columns::ms2_intensity:
            m_line.ms2_intensity = cell.getDoubleValue();
            break;
          default:
            qDebug() << "m_line.calcmass=" << m_line.calcmass;
            throw pappso::ExceptionNotImplemented(
              QObject::tr("column type %1 not implemented").arg((std::uint8_t)column_type));
            break;
        }

      /*

    2333	TMISDSDYTEFENFTK
    GRMZM2G018197_P01;GRMZM2G068952_P01;GRMZM5G822976_P01	3
    20120906_balliau_extract_1_A01_urnb-1.mzML	controllerType=0
    controllerNumber=1 scan=12542	1	1	1926.8225	1926.8193	2	16	0	0
    0.0	1.6471838	1.9796097	54.06803492297634	28.049970383419556	0.0	38.192993
    0.76385987	0.7671368	0.0032769442	0.0	0.0	0.0	16	2	14	0.875	32.54396 380
    -13.375352220427656	1.1570586	-34.13482	0.00016041065	0.00022231363
    0.00040124074	1271951.1
    */
    }
  m_columnNumber++;
}

void
SageTsvHandler::startLine()
{
  m_columnNumber = 0;
  msp_peptide    = nullptr;
  m_proteinList.clear();
  m_line = Line();
}

void
SageTsvHandler::endDocument()
{
}
void
SageTsvHandler::startSheet(const QString &sheet_name [[maybe_unused]])
{
  m_columnNumber = 0;
  m_lineNumber   = 0;
  mp_monitor->setStatus(QObject::tr("reading Sage TSV file"));

  if(mp_monitor->shouldIstop())
    {
      throw pappso::ExceptionInterrupted(QObject::tr("Sage TSV data reading process interrupted"));
    }
}

void
SageTsvHandler::parsePeptide(const QString &peptide_str)
{
  qDebug();
  QString peptide_str_verif = peptide_str;
  // fixed modifications :
  for(SageReader::SageModification modif : m_staticModificationList)
    {
      qDebug() << modif.strModification;
      qDebug() << modif.modification->getAccession();
      peptide_str_verif = peptide_str_verif.replace(
        modif.strModification, QString("[%1]").arg(modif.modification->getAccession()));
    }
  // variable modifications :
  for(SageReader::SageModification modif : m_variableModificationList)
    {
      qDebug() << modif.strModification;
      qDebug() << modif.modification->getAccession();
      peptide_str_verif = peptide_str_verif.replace(
        modif.strModification, QString("[%1]").arg(modif.modification->getAccession()));
    }

  qDebug() << peptide_str_verif;
  // LPMFGC[+57.0216]NDATQVYK
  msp_peptide = pappso::PeptideProFormaParser::parseString(peptide_str_verif);
  qDebug();
  // variable modifications :
  /*
  setVariableModifications(peptide_sp,
                           peptide_line.peptide_string_list.at(6));
*/
  qDebug() << msp_peptide.get()->toProForma();
}


void
SageTsvHandler::parseProteins(const QString &proteins_str)
{
  m_proteinList.clear();
  m_proteinList = proteins_str.split(";");
  for(QString accession : m_proteinList)
    {

      PsmProtein psm_protein;
      psm_protein.protein_sp = std::make_shared<pappso::Protein>(accession, "");
      psm_protein.isTarget   = true;
      if(accession.startsWith(m_decoyTag))
        {
          psm_protein.isTarget = false;
        }

      m_psmProteinMap.insert(psm_protein);
    }
}

bool
SageTsvHandler::parseScanNrColumn(const QString &spectrum_string_id)
{
  m_spectrumNativeId = spectrum_string_id;
  qDebug() << spectrum_string_id;
  m_scanNumber     = 0;
  bool is_ok       = false;
  m_scanNumberIsOk = false;
  // controllerType=0 controllerNumber=1 scan=176056
  if(mp_currentSample->name.endsWith(".d"))
    { // assume this is a Bruker's timTOF sample
      // 200ngHeLaPASEF_2min_compressed.d	2005
      std::size_t precursor_number = spectrum_string_id.toULongLong(&is_ok);
      if(is_ok)
        {
          // precursor=2006 idxms2=4011
          m_scanNumberIsOk   = is_ok;
          m_spectrumIndex    = precursor_number * 2 + 1;
          m_spectrumNativeId = QString("precursor=%1 idxms2=%2")
                                 .arg(precursor_number + 1)
                                 .arg(precursor_number * 2 + 1);
        }
    }

  if(!is_ok)
    {
      QStringList scan_list = spectrum_string_id.split("scan=");
      if(scan_list.size() == 2)
        {
          // we bet that there is a scan number, easy to parse
          m_scanNumber     = scan_list.at(1).toULongLong(&is_ok);
          m_scanNumberIsOk = is_ok;
          if(m_scanNumber > 0)
            m_spectrumIndex = m_scanNumber - 1;
        }
      if(is_ok == false)
        {
          ;
          QStringList scan_list = spectrum_string_id.split(QRegularExpression("[^\\d]"));
          if(scan_list.size() == 2)
            {
              m_scanNumber     = scan_list.at(0).toULongLong(&is_ok);
              m_scanNumberIsOk = is_ok;
              if(m_scanNumber > 0)
                m_spectrumIndex = m_scanNumber - 1;
            }
          /*
         if(msp_previousMsrun != msp_msrun)
           {
             mp_monitor->setStatus(
               QObject::tr("Reading mz data file %1").arg(msp_msrun.get()->getFileName()));
             msp_previousMsrun = msp_msrun;
           }

         pappso::MsRunReader *msrunreader_p = msp_msrun.get()->getMsRunReaderSPtr().get();
         if(msrunreader_p->getMsRunId()->getMsDataFormat() == pappso::MsDataFormat::brukerTims)
           {
             m_spectrumIndex = spectrum_string_id.toInt() * 2 - 1;
           }
         else
           {
             m_spectrumIndex =
               msrunreader_p->spectrumStringIdentifier2SpectrumIndex(spectrum_string_id);
           }*/
        }
    }
  qDebug() << spectrum_string_id;
  return is_ok;
}

void
SageTsvHandler::parseMsRunFilename(const QString &msrun_filename)
{


  // find the sample :
  auto it_insert   = m_sampleMap.insert({msrun_filename, {}});
  mp_currentSample = &(it_insert.first->second);
  if(it_insert.second)
    {
      // new sample
      it_insert.first->second.name = msrun_filename;
      QCborMap ms_file;
      QCborMap identification_file;

      it_insert.first->second.cbor_core_sample.insert(QString("name"),
                                                      QFileInfo(msrun_filename).baseName());

      // identification_file_list
      QCborArray identification_file_list;
      identification_file.insert(QString("name"), m_sageReader.getmJsonAbsoluteFilePath());
      identification_file_list.push_back(identification_file);
      it_insert.first->second.cbor_core_sample.insert(QString("identification_file_list"),
                                                      identification_file_list);


      ms_file.insert(QString("name"), m_sageReader.getMzmlPath(msrun_filename));
      it_insert.first->second.cbor_core_sample.insert(QString("peaklist_file"), ms_file);
    }

  /*
  msp_msrun = m_sageReader.getSageFileReader().getMsRunSpWithFileName(msrun_filename);
  qDebug() << msp_msrun.get()->getFileName();

  msp_identificationSageJsonFileSp =
    m_sageReader.getSageFileReader().getIdentificationSageJsonFileSpWithFileName(msrun_filename);

  mp_identificationGroup =
    m_sageReader.getSageFileReader().getIdentificationGroupPtrWithFileName(msrun_filename);
  qDebug() << msp_msrun.get()->getFileName();
  */
}

void
SageTsvHandler::recordLine()
{
  qDebug();

  for(const QString &accession : m_proteinList)
    {
      PsmProtein psm_protein;
      psm_protein.protein_sp = std::make_shared<pappso::Protein>(accession, "");

      QCborMap sage_eval;
      sage_eval.insert(QString("protein_q"), m_line.protein_q);

      auto it = m_psmProteinMap.insert(psm_protein);
      it.first->second.cborEval.insert(QString("sage"), sage_eval);
    }
  /*
  PeptideEvidence pe(msp_msrun.get(), m_spectrumIndex, true);
  pe.setCharge(m_line.charge);
  pe.setChecked(true);
  pe.setExperimentalMass(m_line.expmass);
  pe.setPeptideXtpSp(msp_peptide);
  pe.setIdentificationDataSource(msp_identificationSageJsonFileSp.get());
  pe.setIdentificationEngine(m_identificationEngine);
  pe.setRetentionTime(m_line.rt);
  pe.setParam(PeptideEvidenceParam::tandem_hyperscore, m_line.hyperscore);
  pe.setParam(PeptideEvidenceParam::sage_sage_discriminant_score,
              m_line.sage_discriminant_score);
  pe.setParam(PeptideEvidenceParam::sage_peptide_q, m_line.peptide_q);
  pe.setParam(PeptideEvidenceParam::sage_posterior_error, m_line.posterior_error);
  pe.setParam(PeptideEvidenceParam::sage_spectrum_q, m_line.spectrum_q);
  pe.setParam(PeptideEvidenceParam::sage_predicted_rt, m_line.predicted_rt);
  pe.setParam(PeptideEvidenceParam::sage_isotope_error, m_line.isotope_error);


  PeptideMatch peptide_match;
  // peptide_match.setStart(mz_peptide_evidence.start);
  peptide_match.setPeptideEvidenceSp(
    msp_identificationSageJsonFileSp.get()->getPeptideEvidenceStore().getInstance(&pe));
    */

  // find the scan in sample
  auto it_insert            = mp_currentSample->scan_map.insert({m_spectrumNativeId, Scan()});
  Scan *current_cbor_scan_p = &(it_insert.first->second);
  if(it_insert.second)
    {
      // new scan
      QCborMap &scan_id = it_insert.first->second.cbor_id;
      if(m_scanNumberIsOk)
        {
          scan_id.insert(QString("index"), (qint64)m_spectrumIndex);
          if(m_scanNumber > 0)
            scan_id.insert(QString("scan"), (qint64)m_scanNumber);
        }
      scan_id.insert(QString("native_id"), m_spectrumNativeId);

      QCborMap &scan_ms2 = it_insert.first->second.cbor_ms2;
      scan_ms2.insert(QString("rt"), m_line.rt);

      QCborMap &scan_precursor = it_insert.first->second.cbor_precursor;
      scan_precursor.insert(QString("z"), m_line.charge);
      double mh = m_line.expmass + MHPLUS;
      scan_precursor.insert(QString("mh"), mh);
      double exp_mz = (m_line.expmass + (MHPLUS * m_line.charge)) / m_line.charge;
      scan_precursor.insert(QString("mz"), exp_mz);
    }

  Psm one_psm;
  one_psm.peptide_sequence_li = msp_peptide.get()->getSequenceLi();
  one_psm.proforma            = msp_peptide.get()->toProForma();
  one_psm.protein_list        = m_proteinList;


  one_psm.cbor_eval.insert(QString("aligned_rt"), m_line.aligned_rt);
  one_psm.cbor_eval.insert(QString("calcmass"), m_line.calcmass);
  one_psm.cbor_eval.insert(QString("delta_best"), m_line.delta_best);
  one_psm.cbor_eval.insert(QString("delta_mobility"), m_line.delta_mobility);
  one_psm.cbor_eval.insert(QString("delta_next"), m_line.delta_next);
  one_psm.cbor_eval.insert(QString("delta_rt_model"), m_line.delta_rt_model);
  one_psm.cbor_eval.insert(QString("fragment_ppm"), m_line.fragment_ppm);
  one_psm.cbor_eval.insert(QString("hyperscore"), m_line.hyperscore);
  one_psm.cbor_eval.insert(QString("ion_mobility"), m_line.ion_mobility);
  one_psm.cbor_eval.insert(QString("isotope_error"), m_line.isotope_error);
  one_psm.cbor_eval.insert(QString("label"), m_line.label);
  one_psm.cbor_eval.insert(QString("longest_b"), (qint64)m_line.longest_b);
  one_psm.cbor_eval.insert(QString("longest_y"), (qint64)m_line.longest_y);
  one_psm.cbor_eval.insert(QString("longest_y_pct"), m_line.longest_y_pct);
  one_psm.cbor_eval.insert(QString("matched_intensity_pct"), m_line.matched_intensity_pct);
  one_psm.cbor_eval.insert(QString("matched_peaks"), (qint64)m_line.matched_peaks);
  one_psm.cbor_eval.insert(QString("missed_cleavages"), m_line.missed_cleavages);
  one_psm.cbor_eval.insert(QString("ms2_intensity"), m_line.ms2_intensity);
  one_psm.cbor_eval.insert(QString("peptide_len"), (qint64)m_line.peptide_len);
  one_psm.cbor_eval.insert(QString("peptide_q"), m_line.peptide_q);
  one_psm.cbor_eval.insert(QString("poisson"), m_line.poisson);
  one_psm.cbor_eval.insert(QString("posterior_error"), m_line.posterior_error);
  one_psm.cbor_eval.insert(QString("precursor_ppm"), m_line.precursor_ppm);
  one_psm.cbor_eval.insert(QString("predicted_mobility"), m_line.predicted_mobility);
  one_psm.cbor_eval.insert(QString("predicted_rt"), m_line.predicted_rt);
  one_psm.cbor_eval.insert(QString("protein_q"), m_line.protein_q);
  one_psm.cbor_eval.insert(QString("rank"), m_line.rank);
  one_psm.cbor_eval.insert(QString("sage_discriminant_score"), m_line.sage_discriminant_score);
  one_psm.cbor_eval.insert(QString("scored_candidates"), (qint64)m_line.scored_candidates);
  one_psm.cbor_eval.insert(QString("semi_enzymatic"), m_line.semi_enzymatic);
  one_psm.cbor_eval.insert(QString("spectrum_q"), m_line.spectrum_q);

  current_cbor_scan_p->psm_list.emplace_back(one_psm);


  std::size_t progress = m_lineNumber / 10000;
  if(progress > m_progressIndex)
    {
      if(mp_monitor->shouldIstop())
        {
          throw pappso::ExceptionInterrupted(
            QObject::tr("Sage TSV data reading process interrupted"));
        }
      m_progressIndex = progress;
      mp_monitor->setStatus(QString("%1K ").arg(m_progressIndex * 10));
    }
}

void
SageTsvHandler::writeSample(const SageTsvHandler::Sample &one_sample)
{
  m_sageReader.getCborStreamWriter().startMap();
  m_sageReader.getCborStreamWriter().append("name");
  one_sample.cbor_core_sample.value("name").toCbor(m_sageReader.getCborStreamWriter());

  m_sageReader.getCborStreamWriter().append("identification_file_list");
  one_sample.cbor_core_sample.value("identification_file_list")
    .toCbor(m_sageReader.getCborStreamWriter());


  m_sageReader.getCborStreamWriter().append("peaklist_file");
  one_sample.cbor_core_sample.value("peaklist_file").toCbor(m_sageReader.getCborStreamWriter());
  //"scan_list": [

  m_sageReader.getCborStreamWriter().append("scan_list");
  m_sageReader.getCborStreamWriter().startArray(one_sample.scan_map.size());
  for(auto &it_scan : one_sample.scan_map)
    {
      writeScan(it_scan.second);
    }
  m_sageReader.getCborStreamWriter().endArray();


  m_sageReader.getCborStreamWriter().endMap();
}

void
SageTsvHandler::writeScan(const SageTsvHandler::Scan &one_scan)
{
  m_sageReader.getCborStreamWriter().startMap();
  m_sageReader.getCborStreamWriter().append("id");
  QCborValue(one_scan.cbor_id).toCbor(m_sageReader.getCborStreamWriter());
  m_sageReader.getCborStreamWriter().append("precursor");
  QCborValue(one_scan.cbor_precursor).toCbor(m_sageReader.getCborStreamWriter());
  m_sageReader.getCborStreamWriter().append("ms2");
  QCborValue(one_scan.cbor_ms2).toCbor(m_sageReader.getCborStreamWriter());

  m_sageReader.getCborStreamWriter().append("psm_list");
  m_sageReader.getCborStreamWriter().startArray(one_scan.psm_list.size());
  for(auto &it_psm : one_scan.psm_list)
    {
      writePsm(it_psm);
    }
  m_sageReader.getCborStreamWriter().endArray();

  m_sageReader.getCborStreamWriter().endMap();
}

void
SageTsvHandler::writePsm(const SageTsvHandler::Psm &one_psm)
{
  m_sageReader.getCborStreamWriter().startMap();
  m_sageReader.getCborStreamWriter().append("proforma");
  m_sageReader.getCborStreamWriter().append(one_psm.proforma);
  m_sageReader.getCborStreamWriter().append("protein_list");

  QCborArray cbor_protein_list;
  for(const QString &accession : one_psm.protein_list)
    {
      // qWarning() << "accession=" << accession;
      QCborMap cbor_protein;
      cbor_protein.insert(QString("accession"), accession);


      // start/end positions
      QString protein_sequence =
        QString(m_psmProteinMap.getByAccession(accession).protein_sp.get()->getSequence())
          .replace("L", "I");
      int position = protein_sequence.indexOf(one_psm.peptide_sequence_li);

      QCborArray positions;
      while(position >= 0)
        {
          positions.push_back(position);
          position = protein_sequence.indexOf(one_psm.peptide_sequence_li, position + 1);
        }

      cbor_protein.insert(QString("positions"), positions);

      cbor_protein_list.append(cbor_protein);
    }


  QCborValue(cbor_protein_list).toCbor(m_sageReader.getCborStreamWriter());

  m_sageReader.getCborStreamWriter().append("eval");
  m_sageReader.getCborStreamWriter().startMap();
  m_sageReader.getCborStreamWriter().append("sage");
  QCborValue(one_psm.cbor_eval).toCbor(m_sageReader.getCborStreamWriter());
  m_sageReader.getCborStreamWriter().endMap();

  m_sageReader.getCborStreamWriter().endMap();
}


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