/**
 * \file src/input/mzidentml/mzidentmlsaxhandler.cpp
 * \date 14/11/2020
 * \author Olivier Langella
 * \brief parse mzIdentML result file
 */


/*******************************************************************************
 * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of XTPcpp.
 *
 *     XTPcpp 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.
 *
 *     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "mzidentmlsaxhandler.h"
#include <pappsomspp/psm/deepprot/deepprotenum.h>


#include <pappsomspp/exception/exceptionnotfound.h>
#include <cmath>
#include "../../core/peptideevidence.h"
#include "../../utils/peptidestore.h"
#include "../../utils/proteinstore.h"
#include "../../utils/utils.h"
#include "../datafilenotsupportedexception.h"


QString
MzIdentMlSaxHandler::CvParam::toString() const
{
  return QString("%1 %2 %3 %4").arg(cvRef).arg(accession).arg(name).arg(value);
}


QString
MzIdentMlSaxHandler::UserParam::toString() const
{
  return QString("%1 %2").arg(name).arg(value);
}

MzIdentMlSaxHandler::MzIdentMlSaxHandler(
  Project *p_project,
  IdentificationGroup *p_identification_group,
  IdentificationDataSource *p_identification_data_source)
  : mp_project(p_project)
{
  qDebug();
  mp_identificationGroup = p_identification_group;

  mp_identificationDataSource = p_identification_data_source;
  msp_msrun                   = p_identification_data_source->getMsRunSp();
}

MzIdentMlSaxHandler::~MzIdentMlSaxHandler()
{
}

bool
MzIdentMlSaxHandler::startElement(const QString &namespaceURI [[maybe_unused]],
                                  const QString &localName [[maybe_unused]],
                                  const QString &qName,
                                  const QXmlAttributes &attributes)
{
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  m_tagStack.push_back(qName);
  bool is_ok = true;

  try
    {
      // startElement_group
      if(m_tagStack.size() == 1)
        {
          if(qName != "MzIdentML")
            {
              throw DataFileNotSupportedException(
                QObject::tr("ERROR this file is not an MzIdentML file %1")
                  .arg(qName));
            }
        }
      else if(qName == "AnalysisSoftware")
        {
          is_ok = startElement_AnalysisSoftware(attributes);
        }
      else if(qName == "DBSequence")
        {
          is_ok = startElement_DBSequence(attributes);
        }
      else if(qName == "cvParam")
        {
          is_ok = startElement_cvParam(attributes);
        }
      else if(qName == "userParam")
        {
          is_ok = startElement_userParam(attributes);
        }
      else if(qName == "Peptide")
        {
          is_ok = startElement_Peptide(attributes);
        }
      else if(qName == "Modification")
        {
          is_ok = startElement_Modification(attributes);
        }
      else if(qName == "PeptideEvidence")
        {
          is_ok = startElement_PeptideEvidence(attributes);
        }
      else if(qName == "SearchDatabase")
        {
          is_ok = startElement_SearchDatabase(attributes);
        }
      else if(qName == "SpectraData")
        {
          is_ok = startElement_SpectraData(attributes);
        }
      else if(qName == "SpectrumIdentificationResult")
        {
          is_ok = startElement_SpectrumIdentificationResult(attributes);
        }
      else if(qName == "SpectrumIdentificationItem")
        {
          is_ok = startElement_SpectrumIdentificationItem(attributes);
        }
      else if(qName == "PeptideEvidenceRef")
        {
          is_ok = startElement_PeptideEvidenceRef(attributes);
        }


      m_currentText.clear();
    }
  catch(const pappso::PappsoException &exception_pappso)
    {
      m_errorStr = QObject::tr(
                     "ERROR in MzIdentMlSaxHandler::startElement tag "
                     "%1, PAPPSO exception:\n%2")
                     .arg(qName)
                     .arg(exception_pappso.qwhat());
      return false;
    }
  catch(const std::exception &exception_std)
    {
      m_errorStr = QObject::tr(
                     "ERROR in MzIdentMlSaxHandler::startElement tag %1, std "
                     "exception:\n%2")
                     .arg(qName)
                     .arg(exception_std.what());
      return false;
    }
  if(!is_ok)
    {
      m_errorStr = QObject::tr(
                     "ERROR in MzIdentMlSaxHandler::startElement tag "
                     "%1 :\n%2")
                     .arg(qName)
                     .arg(m_errorStr);
    }
  return is_ok;
}

bool
MzIdentMlSaxHandler::endElement(const QString &namespaceURI [[maybe_unused]],
                                const QString &localName [[maybe_unused]],
                                const QString &qName)
{

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  bool is_ok = true;
  // endElement_peptide_list
  try
    {
      if(qName == "cvParam")
        {
          is_ok = endElement_cvParam();
        }
      else if(qName == "userParam")
        {
          is_ok = endElement_userParam();
        }
      else if(qName == "PeptideSequence")
        {
          is_ok = endElement_PeptideSequence();
        }
      else if(qName == "Peptide")
        {
          is_ok = endElement_Peptide();
        }
      else if(qName == "Modification")
        {
          is_ok = endElement_Modification();
        }
      else if(qName == "SpectrumIdentificationResult")
        {
          is_ok = endElement_SpectrumIdentificationResult();
        }
      else if(qName == "AnalysisSoftware")
        {
          is_ok = endElement_AnalysisSoftware();
        }
      else if(qName == "Seq")
        {
          is_ok = endElement_Seq();
        }

      // end of detection_moulon
      // else if ((_tag_stack.size() > 1) &&
      //         (_tag_stack[_tag_stack.size() - 2] == "detection_moulon"))
    }
  catch(pappso::PappsoException &exception_pappso)
    {
      m_errorStr = QObject::tr(
                     "ERROR in MzIdentMlSaxHandler::endElement tag %1, "
                     "PAPPSO exception:\n%2")
                     .arg(qName)
                     .arg(exception_pappso.qwhat());
      return false;
    }
  catch(std::exception &exception_std)
    {
      m_errorStr =
        QObject::tr(
          "ERROR in MzIdentMlSaxHandler::endElement tag %1, std exception:\n%2")
          .arg(qName)
          .arg(exception_std.what());
      return false;
    }

  m_currentText.clear();
  m_tagStack.pop_back();

  if(!is_ok)
    {
      m_errorStr =
        QObject::tr("ERROR in MzIdentMlSaxHandler::endElement tag %1 :\n%2")
          .arg(qName)
          .arg(m_errorStr);
    }
  return is_ok;
}


bool
MzIdentMlSaxHandler::startElement_AnalysisSoftware(QXmlAttributes attributes)
{

  bool is_ok = true;
  // <AnalysisSoftware version="0.0.9" name="DeepProt" id="as1">

  if(attributes.value("name") == "DeepProt")
    {
      m_analysisSotwareNameFound = IdentificationEngine::DeepProt;
    }


  m_currentAnalysisSoftwareId = attributes.value("id");


  return is_ok;
}

/*
 * <SequenceCollection xmlns="http://psidev.info/psi/pi/mzIdentML/1.1">
<DBSequence length="834" searchDatabase_ref="SearchDB_1"
accession="GRMZM2G073054_P01" id="DBSeq19404345"> <cvParam cvRef="PSI-MS"
accession="MS:1001088" name="protein description" value="GRMZM2G073054_P01
O24421 Starch branching enzyme IIa Fragment (EC 2.4.1.18) seq=translation;
coord=2:58586007..58596234:1; parent_transcript=GRMZM2G073054_T01;
parent_gene=GRMZM2G073054"/>
</DBSequence>
*/

bool
MzIdentMlSaxHandler::startElement_DBSequence(QXmlAttributes attributes)
{
  bool is_ok = true;
  // attributes.value("base_name")
  // ProteinXtpSp sp_xtp_protein = _current_protein.makeProteinXtpSp();
  m_currentProtein = ProteinXtp().makeProteinXtpSp();
  m_currentProtein.get()->setAccession(attributes.value("accession"));

  m_currentProtein =
    mp_project->getProteinStore().getInstance(m_currentProtein);


  FastaFileSp fastaFile = mp_project->getFastaFileStore().getInstance(
    FastaFile(attributes.value("searchDatabase_ref")));

  mp_identificationDataSource->addFastaFile(fastaFile);
  m_FastaFileIdMap.insert(std::pair<QString, FastaFileSp>(
    attributes.value("searchDatabase_ref"), fastaFile));

  // searchDatabase_ref="SearchDB_1"

  m_currentProtein.get()->setFastaFileP(fastaFile.get());

  m_ProteinXmlIdMap.insert(
    std::pair<QString, ProteinXtpSp>(attributes.value("id"), m_currentProtein));
  return is_ok;
}


//<cvParam cvRef="PSI-MS" accession="MS:1001088" name="protein description"
// value="GRMZM2G073054_P01 O24421 Starch branching enzyme IIa Fragment
//(EC 2.4.1.18) seq=translation; coord=2:58586007..58596234:1;
// parent_transcript=GRMZM2G073054_T01; parent_gene=GRMZM2G073054"/>


//  cvParam cvRef="PSI-MS" accession="MS:1000016"
//        name="scan start time" value="3527.73" unitAccession="UO:0000010"
//        unitName="second" unitCvRef="UO"

bool
MzIdentMlSaxHandler::startElement_cvParam(QXmlAttributes attributes)
{
  bool is_ok                     = true;
  m_currentCvParam.cvRef         = attributes.value("cvRef");
  m_currentCvParam.accession     = attributes.value("accession");
  m_currentCvParam.name          = attributes.value("name");
  m_currentCvParam.value         = attributes.value("value");
  m_currentCvParam.unitAccession = attributes.value("unitAccession");
  m_currentCvParam.unitName      = attributes.value("unitName");
  m_currentCvParam.unitCvRef     = attributes.value("unitCvRef");
  return is_ok;
}

bool
MzIdentMlSaxHandler::startElement_userParam(QXmlAttributes attributes)
{

  bool is_ok               = true;
  m_currentUserParam.name  = attributes.value("name");
  m_currentUserParam.value = attributes.value("value");
  return is_ok;
}


//<PeptideEvidence dBSequence_ref="DBSeq19404345"
// peptide_ref="Pep_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK" start="84"
// end="123" pre="K" post="I" isDecoy="false"
// id="PepEv_19404428_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK_84"/>


bool
MzIdentMlSaxHandler::startElement_PeptideEvidence(QXmlAttributes attributes)
{
  bool is_ok = true;

  MzidPeptideEvidence pe;

  auto itprot = m_ProteinXmlIdMap.find(attributes.value("dBSequence_ref"));
  if(itprot == m_ProteinXmlIdMap.end())
    {
      m_errorStr = QObject::tr("dBSequence_ref %1 not defined")
                     .arg(attributes.value("dBSequence_ref"));
      return false;
    }

  pe.protein = itprot->second;


  auto itpep = m_PeptideIdMap.find(attributes.value("peptide_ref"));
  if(itpep == m_PeptideIdMap.end())
    {
      m_errorStr = QObject::tr("peptide_ref %1 not defined")
                     .arg(attributes.value("peptide_ref"));
      return false;
    }

  pe.peptide = itpep->second;

  pe.start   = attributes.value("start").toUInt() - 1;
  pe.end     = attributes.value("end").toUInt() - 1;
  pe.isDecoy = false;
  if(attributes.value("isDecoy") == "true")
    {
      pe.isDecoy = true;
    }

  m_MzidPeptideEvidenceIdMap.insert(
    std::pair<QString, MzidPeptideEvidence>(attributes.value("id"), pe));
  return is_ok;
}

//<SearchDatabase numDatabaseSequences="136828"
// location="/home/thierry/test/MS-GF+/Genome_Z_mays_v5a_conta.fasta"
// id="SearchDB_1">
//      <FileFormat>
//        <cvParam cvRef="PSI-MS" accession="MS:1001348" name="FASTA format"/>
//      </FileFormat>
//      <DatabaseName>
//        <userParam name="Genome_Z_mays_v5a_conta.fasta"/>
//      </DatabaseName>
//    </SearchDatabase>

bool
MzIdentMlSaxHandler::startElement_SearchDatabase(QXmlAttributes attributes)
{
  bool is_ok = true;

  auto itfasta = m_FastaFileIdMap.find(attributes.value("id"));

  if(itfasta == m_FastaFileIdMap.end())
    {
      m_errorStr = QObject::tr("SearchDatabase id %1 not defined")
                     .arg(attributes.value("id"));
      return false;
    }

  itfasta->second.get()->setFastaSource(attributes.value("location"));

  return is_ok;
}


//<SpectraData
// location="/home/thierry/test/MS-GF+/20170308_test_dig_Liquide_01.mzXML"
// id="SID_1" name="20170308_test_dig_Liquide_01.mzXML">
//      <FileFormat>
//        <cvParam cvRef="PSI-MS" accession="MS:1000566" name="ISB mzXML file"/>
//      </FileFormat>
//      <SpectrumIDFormat>
//        <cvParam cvRef="PSI-MS" accession="MS:1000776" name="scan number only
//        nativeID format"/>
//      </SpectrumIDFormat>
//    </SpectraData>

bool
MzIdentMlSaxHandler::startElement_SpectraData(QXmlAttributes attributes)
{
  bool is_ok = true;

  MsRunSp msrun =
    mp_project->getMsRunStore().getInstance(attributes.value("location"));


  //msrun.get()->setXmlId(attributes.value("id"));
  msrun.get()->setSampleName(attributes.value("name"));


  m_MsRunIdMap.insert(
    std::pair<QString, MsRunSp>(attributes.value("id"), msrun));

  return is_ok;
}


//      <SpectrumIdentificationResult spectrumID="scan=16079"
//      spectraData_ref="SID_1" id="SIR_16079">
//        <SpectrumIdentificationItem chargeState="3"
//        experimentalMassToCharge="1288.6282958984375"
//        calculatedMassToCharge="1288.6258544921875"
//        peptide_ref="Pep_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK" rank="1"
//        passThreshold="true" id="SII_16079_1">
//          <PeptideEvidenceRef
//          peptideEvidence_ref="PepEv_19404428_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK_84"/>
//          <PeptideEvidenceRef
//          peptideEvidence_ref="PepEv_19405273_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK_94"/>
//          <cvParam cvRef="PSI-MS" accession="MS:1002049" name="MS-GF:RawScore"
//          value="356"/> <cvParam cvRef="PSI-MS" accession="MS:1002050"
//          name="MS-GF:DeNovoScore" value="369"/> <cvParam cvRef="PSI-MS"
//          accession="MS:1002052" name="MS-GF:SpecEValue"
//          value="9.149361665076834E-40"/> <cvParam cvRef="PSI-MS"
//          accession="MS:1002053" name="MS-GF:EValue"
//          value="2.057944235338586E-32"/>
//         <userParam name="IsotopeError" value="0"/>
//         <userParam name="AssumedDissociationMethod" value="HCD"/>
//        </SpectrumIdentificationItem>
//        <cvParam cvRef="PSI-MS" accession="MS:1001115" name="scan number(s)"
//        value="16079"/> <cvParam cvRef="PSI-MS" accession="MS:1000016"
//        name="scan start time" value="3527.73" unitAccession="UO:0000010"
//        unitName="second" unitCvRef="UO"/>
//      </SpectrumIdentificationResult>


bool
MzIdentMlSaxHandler::startElement_SpectrumIdentificationResult(
  QXmlAttributes attributes)
{
  bool is_ok = true;
  m_currentSpectrumIdentificationResult.cvParamList.clear();
  m_currentSpectrumIdentificationResult.userParamList.clear();
  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.clear();

  m_currentSpectrumIdentificationResult.spectrumID =
    attributes.value("spectrumID");
  m_currentSpectrumIdentificationResult.id = attributes.value("id");


  auto itmsrun = m_MsRunIdMap.find(attributes.value("spectraData_ref"));

  if(itmsrun == m_MsRunIdMap.end())
    {
      m_errorStr = QObject::tr("spectraData_ref %1 not defined")
                     .arg(attributes.value("spectraData_ref"));
      return false;
    }
  m_currentSpectrumIdentificationResult.msrun = itmsrun->second;

  return is_ok;
}


//        < chargeState="3"
//        experimentalMassToCharge="1288.6282958984375"
//        calculatedMassToCharge="1288.6258544921875"
//        peptide_ref="Pep_TSSSPTQTTSAVAEASSGVEAEERPELSEVIGVGGTGGTK" rank="1"
//        passThreshold="true" id="SII_16079_1">

bool
MzIdentMlSaxHandler::startElement_SpectrumIdentificationItem(
  QXmlAttributes attributes)
{
  bool is_ok = true;

  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList
    .push_back(SpectrumIdentificationItem());
  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .mzidPeptideEvidenceList.clear();
  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .cvParamList.clear();
  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .userParamList.clear();

  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .chargeState = attributes.value("chargeState").toUInt();

  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .experimentalMassToCharge =
    attributes.value("experimentalMassToCharge").toDouble();


  auto itpeptide = m_PeptideIdMap.find(attributes.value("peptide_ref"));

  if(itpeptide == m_PeptideIdMap.end())
    {
      m_errorStr = QObject::tr("peptide_ref %1 not defined")
                     .arg(attributes.value("peptide_ref"));
      return false;
    }
  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .peptide = itpeptide->second;

  return is_ok;
}


// <Peptide id="pep0">
//    <PeptideSequence>AIGSIIAYITDAK</PeptideSequence>
//      <Modification monoisotopicMassDelta="-0.02682025649" location="1">
//          <cvParam accession="-0.0268203" cvRef="PSI-MOD" name=""/>
//      </Modification>
//  </Peptide>

bool
MzIdentMlSaxHandler::startElement_Peptide(QXmlAttributes attributes)
{
  bool is_ok = true;
  // attributes.value("base_name")
  // ProteinXtpSp sp_xtp_protein = _current_protein.makeProteinXtpSp();
  m_currentPeptide = nullptr;

  m_currentPeptideXmlId = attributes.value("id");
  return is_ok;
}

bool
MzIdentMlSaxHandler::endElement_PeptideSequence()
{
  bool is_ok = true;

  m_currentPeptide = PeptideXtp(m_currentText.simplified()).makePeptideXtpSp();

  return is_ok;
}

bool
MzIdentMlSaxHandler::endElement_Peptide()
{
  bool is_ok = true;

  m_currentPeptide =
    mp_project->getPeptideStore().getInstance(m_currentPeptide);


  m_PeptideIdMap.insert(
    std::pair<QString, PeptideXtpSp>(m_currentPeptideXmlId, m_currentPeptide));

  return is_ok;
}

//<Modification monoisotopicMassDelta="-0.02682025649" location="1">
//               <cvParam accession="-0.0268203" cvRef="PSI-MOD" name=""/>
//           </Modification>

//<Modification location="27" monoisotopicMassDelta="57.021463735">
//      <cvParam cvRef="UNIMOD" accession="UNIMOD:4" name="Carbamidomethyl"/>
//   </Modification>
bool
MzIdentMlSaxHandler::startElement_Modification(QXmlAttributes attributes)
{
  bool is_ok = true;

  m_currentModification.monoisotopicMassDelta =
    attributes.value("monoisotopicMassDelta").toDouble();
  m_currentModification.location = attributes.value("location").toUInt();
  m_currentModification.cvParam  = CvParam();
  return is_ok;
}


bool
MzIdentMlSaxHandler::endElement_SpectrumIdentificationResult()
{
  bool is_ok = true;

  // find scan number
  m_currentSpectrumIdentificationResult.spectrumIndex = 0;
  m_currentSpectrumIdentificationResult.scanNum       = 0;
  m_currentSpectrumIdentificationResult.retentionTime = 0;


  //  <cvParam cvRef="PSI-MS" accession="MS:1001115" name="scan number(s)"
  //        value="16079"/>
  for(auto cvParam : m_currentSpectrumIdentificationResult.cvParamList)
    {
      if(cvParam.accession == "MS:1001115")
        {
          m_currentSpectrumIdentificationResult.scanNum =
            cvParam.value.toUInt();
        }
      else if(cvParam.accession == "MS:1003062")
        {
          m_currentSpectrumIdentificationResult.spectrumIndex =
            cvParam.value.toUInt();
        }
      else if((cvParam.accession == "MS:1000016") ||
              (cvParam.accession == "MS:1000894"))
        {
          //[Term]
          // id: MS:1000894
          // name: retention time
          // def: "A time interval from the start of chromatography when an
          // analyte exits a chromatographic column." [PSI:MS]

          //            [Term]
          // id: MS:1000016
          // name: scan start time
          // def: "The time that an analyzer started a scan, relative to the
          // start of the MS run." [PSI:MS]


          m_currentSpectrumIdentificationResult.retentionTime =
            cvParam.value.toDouble();
        }
    }
  if((m_currentSpectrumIdentificationResult.scanNum == 0) &&
     (m_currentSpectrumIdentificationResult.spectrumIndex == 0))
    {
      m_errorStr = QObject::tr(
                     "scan number or spectrum index not found in "
                     "SpectrumIdentificationResult id %1")
                     .arg(m_currentSpectrumIdentificationResult.id);
      return false;
    }


  if(m_currentSpectrumIdentificationResult.retentionTime == 0)
    {
      m_errorStr =
        QObject::tr(
          "retention time not found in SpectrumIdentificationResult id %1")
          .arg(m_currentSpectrumIdentificationResult.id);
      return false;
    }


  for(auto spectrumIdentificationItem :
      m_currentSpectrumIdentificationResult.spectrumIdentificationItemList)
    {
      return processSpectrumIdentificationItem(spectrumIdentificationItem);
    }

  return is_ok;
}

bool
MzIdentMlSaxHandler::startElement_PeptideEvidenceRef(QXmlAttributes attributes)
{
  bool is_ok = true;
  // peptideEvidence_ref="PepEv_5887424_GFSDSHDGAVTVSLGPSGALAYSAANQSPLVPR_39"


  auto itpeptideEvidence =
    m_MzidPeptideEvidenceIdMap.find(attributes.value("peptideEvidence_ref"));

  if(itpeptideEvidence == m_MzidPeptideEvidenceIdMap.end())
    {
      m_errorStr = QObject::tr("peptideEvidence_ref %1 not defined")
                     .arg(attributes.value("peptideEvidence_ref"));
      return false;
    }

  m_currentSpectrumIdentificationResult.spectrumIdentificationItemList.back()
    .mzidPeptideEvidenceList.push_back(itpeptideEvidence->second);
  return is_ok;
}


bool
MzIdentMlSaxHandler::processSpectrumIdentificationItem(
  const SpectrumIdentificationItem &spectrumIdentificationItem)
{
  bool is_ok = true;


  if(m_currentSpectrumIdentificationResult.msrun.get() != msp_msrun.get())
    {
      m_errorStr =
        QObject::tr(
          "m_currentSpectrumIdentificationResult.msrun.get() \n%1\n != "
          "msp_msrun.get() \n%2\n")
          .arg(m_currentSpectrumIdentificationResult.msrun.get()->toString())
          .arg(msp_msrun.get()->toString());
      return false;
    }

  bool isSpectrumIndex = false;
  if(m_currentSpectrumIdentificationResult.spectrumIndex != 0)
    {
      m_currentSpectrumIdentificationResult.scanNum =
        m_currentSpectrumIdentificationResult.spectrumIndex;
      isSpectrumIndex = true;
    }
  PeptideEvidence peptide_evidence(
    msp_msrun.get(),
    m_currentSpectrumIdentificationResult.scanNum,
    isSpectrumIndex);
  peptide_evidence.setRetentionTime(
    m_currentSpectrumIdentificationResult.retentionTime);
  peptide_evidence.setCharge(spectrumIdentificationItem.chargeState);
  peptide_evidence.setPeptideXtpSp(spectrumIdentificationItem.peptide);
  qDebug() << peptide_evidence.getPeptideXtpSp().get()->toAbsoluteString();
  peptide_evidence.setChecked(true);
  peptide_evidence.setIdentificationDataSource(mp_identificationDataSource);
  peptide_evidence.setIdentificationEngine(getIdentificationEngine());


  pappso::pappso_double exp_mass =
    spectrumIdentificationItem.experimentalMassToCharge;
  peptide_evidence.setExperimentalMass(exp_mass);


  //          <cvParam cvRef="PSI-MS" accession="MS:1002049"
  //          name="MS-GF:RawScore" value="356"/> <cvParam cvRef="PSI-MS"
  //          accession="MS:1002050" name="MS-GF:DeNovoScore" value="369"/>
  //          <cvParam cvRef="PSI-MS" accession="MS:1002052"
  //          name="MS-GF:SpecEValue" value="9.149361665076834E-40"/> <cvParam
  //          cvRef="PSI-MS" accession="MS:1002053" name="MS-GF:EValue"
  //          value="2.057944235338586E-32"/>
  //         <userParam name="IsotopeError" value="0"/>
  //         <userParam name="AssumedDissociationMethod" value="HCD"/>
  for(auto cvParam : spectrumIdentificationItem.cvParamList)
    {

      //<cvParam accession="MS:1002258" cvRef="PSI-MS" value="7"
      // name="Comet:matched ions"/>
      if(cvParam.accession == "MS:1002049")
        {
          // PSI-MS MS:1002049 MS-GF:RawScore 356
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_raw,
                                    QVariant(cvParam.value.toUInt()));
        }
      else if(cvParam.accession == "MS:1002050")
        {
          // msgfplus_denovo     = 9,  ///< MS:1002050  "MS-GF de novo score."
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_denovo,
                                    QVariant(cvParam.value.toUInt()));
        }

      else if(cvParam.accession == "MS:1002052")
        {
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_SpecEValue,
                                    QVariant(cvParam.value.toDouble()));
        }

      else if(cvParam.accession == "MS:1002053")
        {
          // PSI-MS MS:1002053 MS-GF:EValue
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_EValue,
                                    QVariant(cvParam.value.toDouble()));
        }

      else if(cvParam.accession == "MS:1002054")
        {
          // <cvParam cvRef="PSI-MS" accession="MS:1002054" name="MS-GF:QValue"
          // value="0.0"/>
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_QValue,
                                    QVariant(cvParam.value.toDouble()));
        }
      else if(cvParam.accession == "MS:1002055")
        {
          //  <cvParam cvRef="PSI-MS" accession="MS:1002055"
          //  name="MS-GF:PepQValue" value="0.0"/>
          peptide_evidence.setParam(PeptideEvidenceParam::msgfplus_PepQValue,
                                    QVariant(cvParam.value.toDouble()));
        }
      /*
      msgfplus_energy     = 10, ///< MS:1002051  "MS-GF energy score." [PSI:PI]
      msgfplus_SpecEValue = 11, ///< MS:1002052  "MS-GF spectral E-value."
      [PSI:PI] msgfplus_EValue     = 12, ///< MS:1002053  "MS-GF E-value."
      [PSI:PI] msgfplus_isotope_error = 13, ///< MS-GF isotope error
      comet_xcorr   = 14, ///< MS:1002252  "The Comet result 'XCorr'." [PSI:PI]
      comet_deltacn = 15, ///< MS:1002253  "The Comet result 'DeltaCn'."
      [PSI:PI] comet_deltacnstar = 16, ///< MS:1002254  "The Comet result
      'DeltaCnStar'." [PSI:PI] comet_spscore = 17, ///< MS:1002255  "The Comet
      result 'SpScore'." [PSI:PI] comet_sprank  = 18, ///< MS:1002256  "The
      Comet result 'SpRank'." [PSI:PI] comet_expectation_value = 19, ///<
      MS:1002257  "The Comet result 'Expectation value'." [PSI:PI]
        */
      else
        {
          m_errorStr = QObject::tr("cvParam %1 is not taken into account")
                         .arg(cvParam.toString());
          return false;
        }
    }

  for(auto userParam : spectrumIdentificationItem.userParamList)
    {
      if(userParam.name == "DeepProt:original_count")
        {
          // <userParam name="DeepProt:original_count" value="7"/>
          peptide_evidence.setParam(
            PeptideEvidenceParam::deepprot_original_count,
            QVariant(userParam.value.toUInt()));
        }
      else if(userParam.name == "DeepProt:fitted_count")
        {
          // <userParam name="DeepProt:fitted_count" value="7"/>
          peptide_evidence.setParam(PeptideEvidenceParam::deepprot_fitted_count,
                                    QVariant(userParam.value.toUInt()));
        }
      else if(userParam.name == "DeepProt:match_type")
        {
          peptide_evidence.setParam(
            PeptideEvidenceParam::deepprot_match_type,
            (std::uint8_t)pappso::DeepProtEnumStr::DeepProtMatchTypeFromString(
              userParam.value));
        }
      else if(userParam.name == "DeepProt:status")
        {
          peptide_evidence.setParam(
            PeptideEvidenceParam::deepprot_peptide_candidate_status,
            (std::uint8_t)
              pappso::DeepProtEnumStr::DeepProtPeptideCandidateStatusFromString(
                userParam.value));
        }
      else if(userParam.name == "DeepProt:mass_delta")
        {
          peptide_evidence.setParam(PeptideEvidenceParam::deepprot_mass_delta,
                                    QVariant(userParam.value.toDouble()));
        }
      else if(userParam.name == "DeepProt:delta_positions")
        {
          // DeepProt:delta_positions 4 5 6 7 8 9 10 11 12 13
          peptide_evidence.setParam(
            PeptideEvidenceParam::deepprot_delta_positions, userParam.value);
        }
      // <userParam name="IsotopeError" value="0"/>
      //<userParam name="AssumedDissociationMethod" value="HCD"/>
      /*
    else
      {
        m_errorStr = QObject::tr("userParam %1 is not taken into account")
                       .arg(userParam.toString());
        return false;
      }*/
    }


  for(auto mz_peptide_evidence :
      spectrumIdentificationItem.mzidPeptideEvidenceList)
    {
      PeptideMatch peptide_match;
      peptide_match.setStart(mz_peptide_evidence.start);
      peptide_match.setPeptideEvidenceSp(
        mp_identificationDataSource->getPeptideEvidenceStore().getInstance(
          &peptide_evidence));


      ProteinMatch *p_protein_match =
        mp_identificationGroup->getProteinMatchInstance(
          mz_peptide_evidence.protein.get()->getAccession());

      p_protein_match->setChecked(true);
      // qDebug() << "startElement_protein p_protein_match 3 " <<
      // _p_protein_match;
      p_protein_match->setProteinXtpSp(mz_peptide_evidence.protein);
      p_protein_match->addPeptideMatch(peptide_match);
    }

  return is_ok;
}


bool
MzIdentMlSaxHandler::endElement_Modification()
{
  bool is_ok = true;
  // qDebug() << "startElement_aa ";
  pappso::AaModificationP modif = nullptr;


  qDebug() << m_currentModification.monoisotopicMassDelta;
  if(m_currentModification.cvParam.accession != "")
    {
      qDebug() << m_currentModification.cvParam.accession;
      if(m_currentModification.cvParam.accession.startsWith("UNIMOD:"))
        {
          modif = Utils::translateAaModificationFromUnimod(
            m_currentModification.cvParam.accession);
        }
      else
        {
          // hope it is psi mod:
          if(m_currentModification.cvParam.accession.startsWith("MOD:"))
            {
              modif = pappso::AaModification::getInstance(
                m_currentModification.cvParam.accession);
            }
          else if(m_currentModification.cvParam.accession == "MS:1001460")
            {
              //[Term]
              // id: MS:1001460
              // name: unknown modification
              // def: "This term should be given if the modification was
              // unknown." [PSI:PI] is_a: MS:1001471 ! peptide modification
              // details
            }
          else
            {
              qInfo() << "MzIdentMlSaxHandler::endElement_Modification unknown "
                         "modification "
                      << m_currentModification.cvParam.accession << " "
                      << m_currentModification.cvParam.name;
            }
        }
    }

  if(modif == nullptr)
    {
      modif = Utils::guessAaModificationPbyMonoisotopicMassDelta(
        m_currentModification.monoisotopicMassDelta);
    }

  if(m_currentModification.location == 0)
    {
      m_currentPeptide.get()->addAaModification(modif, 0);
    }
  else
    {
      m_currentPeptide.get()->addAaModification(
        modif, m_currentModification.location - 1);
    }
  return is_ok;
}

bool
MzIdentMlSaxHandler::endElement_cvParam()
{
  bool is_ok = true;

  if(m_tagStack[m_tagStack.size() - 2] == "DBSequence")
    {
      if(m_currentCvParam.accession == "MS:1001088")
        {
          // protein description
          m_currentProtein.get()->setDescription(m_currentCvParam.value);
        }
      else if(m_currentCvParam.accession == "MS:1001195")
        {
          // PSI-MS MS:1001195 decoy DB type reverse
          m_currentProtein.get()->setIsDecoy(true);
        }
      else
        {

          m_errorStr = QObject::tr("unknown cvParam for DBSequence tag : %1")
                         .arg(m_currentCvParam.toString());

          return false;
        }
    }
  else if(m_tagStack[m_tagStack.size() - 2] == "Modification")
    {
      m_currentModification.cvParam = m_currentCvParam;
    }

  else if(m_tagStack[m_tagStack.size() - 2] == "SpectrumIdentificationResult")
    {
      m_currentSpectrumIdentificationResult.cvParamList.push_back(
        m_currentCvParam);
    }
  else if(m_tagStack[m_tagStack.size() - 2] == "SpectrumIdentificationItem")
    {
      m_currentSpectrumIdentificationResult.spectrumIdentificationItemList
        .back()
        .cvParamList.push_back(m_currentCvParam);
    }

  else if(m_tagStack[m_tagStack.size() - 3] == "AnalysisSoftware")
    {
      if(m_currentCvParam.accession == "MS:1002048")
        {
          m_analysisSotwareNameFound = IdentificationEngine::MSGFplus;
        }
    }

  return is_ok;
}

bool
MzIdentMlSaxHandler::endElement_userParam()
{
  bool is_ok = true;
  if(m_tagStack[m_tagStack.size() - 2] == "SpectrumIdentificationResult")
    {
      m_currentSpectrumIdentificationResult.userParamList.push_back(
        m_currentUserParam);
    }
  else if(m_tagStack[m_tagStack.size() - 2] == "SpectrumIdentificationItem")
    {
      m_currentSpectrumIdentificationResult.spectrumIdentificationItemList
        .back()
        .userParamList.push_back(m_currentUserParam);
    }

  return is_ok;
}


bool
MzIdentMlSaxHandler::endElement_AnalysisSoftware()
{
  bool is_ok = true;

  switch(m_analysisSotwareNameFound)
    {
      case IdentificationEngine::DeepProt:
        break;

      case IdentificationEngine::MSGFplus:
        break;

      default:
        m_errorStr = QObject::tr(
                       "identification results from %1 are not supported yet, "
                       "Please contact "
                       "the PAPPSO team.")
                       .arg(Utils::getIdentificationEngineName(
                         m_analysisSotwareNameFound));
        return false;
    }


  m_IdentificationEngineMap.insert(std::pair<QString, IdentificationEngine>(
    m_currentAnalysisSoftwareId, m_analysisSotwareNameFound));
  return is_ok;
}

bool
MzIdentMlSaxHandler::error(const QXmlParseException &exception)
{
  m_errorStr = QObject::tr(
                 "Parse error at line %1, column %2 :\n"
                 "%3")
                 .arg(exception.lineNumber())
                 .arg(exception.columnNumber())
                 .arg(exception.message());

  return false;
}


bool
MzIdentMlSaxHandler::fatalError(const QXmlParseException &exception)
{
  m_errorStr = QObject::tr(
                 "Parse error at line %1, column %2 :\n"
                 "%3")
                 .arg(exception.lineNumber())
                 .arg(exception.columnNumber())
                 .arg(exception.message());
  return false;
}

QString
MzIdentMlSaxHandler::errorString() const
{
  return m_errorStr;
}


bool
MzIdentMlSaxHandler::endDocument()
{
  return true;
}

bool
MzIdentMlSaxHandler::startDocument()
{
  return true;
}

bool
MzIdentMlSaxHandler::characters(const QString &str)
{
  m_currentText += str;
  return true;
}

IdentificationEngine
MzIdentMlSaxHandler::getIdentificationEngine() const
{
  if(m_IdentificationEngineMap.size() == 1)
    {
      return m_IdentificationEngineMap.begin()->second;
    }
  if(m_IdentificationEngineMap.size() == 0)
    {
      return IdentificationEngine::unknown;
    }
  else
    {
      throw pappso::PappsoException(
        "Unable to read MzIdentML file containing multiple identification "
        "engines results");
    }
}

bool
MzIdentMlSaxHandler::endElement_Seq()
{
  m_currentProtein.get()->setSequence(m_currentText);
  return true;
}
