/**
 * \file output/proticdbml.cpp
 * \date 11/5/2017
 * \author Olivier Langella
 * \brief PROTICdbML writer
 */

/*******************************************************************************
 * Copyright (c) 2017 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
 *     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/>.
 *
 * Contributors:
 *     Olivier Langella <olivier.langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

#include "proticdbml.h"
#include <pappsomspp/pappsoexception.h>
#include <pappsomspp/utils.h>
#include <pappsomspp/grouping/grpprotein.h>
#include <pappsomspp/amino_acid/aa.h>
#include "../core/msrun.h"
#include "../config.h"
#include "../utils/utils.h"

ProticdbMl::ProticdbMl(const QString &out_filename)
{
  //_p_digestion_pipeline = p_digestion_pipeline;

  //_mzidentml = "http://psidev.info/psi/pi/mzIdentML/1.1";
  QString complete_out_filename = out_filename;
  _output_file                  = new QFile(complete_out_filename);

  if(_output_file->open(QIODevice::WriteOnly))
    {
      _output_stream = new QXmlStreamWriter();
      _output_stream->setDevice(_output_file);
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("error : cannot open the PROTICdbML output file : %1\n")
          .arg(out_filename));
    }

  _output_stream->setAutoFormatting(true);
  _output_stream->writeStartDocument("1.0");
}

ProticdbMl::~ProticdbMl()
{
  delete _output_file;
  delete _output_stream;
}

void
ProticdbMl::close()
{
  _output_stream->writeEndDocument();
  _output_file->close();

  qDebug() << "ProticdbMl::close end duration = " << _duracel.elapsed() << "ms";
}


void
ProticdbMl::write(ProjectSp sp_project)
{

  qDebug() << "ProticdbMl::write begin";
  _duracel.start();
  _sp_project = sp_project;
  if(_sp_project.get() == nullptr)
    {
      throw pappso::PappsoException(
        QObject::tr("Error writing PROTICdbML file :\n project is empty"));
    }


  _output_stream->writeNamespace("http://www.w3.org/2001/XMLSchema-instance",
                                 "xsi");
  // writer.setDefaultNamespace(namespaceURI);
  _output_stream->writeStartElement("PROTICdb");
  //_output_stream->writeAttribute("xmlns","http://pappso.inra.fr/xsd/masschroqml/2.2");
  //_output_stream->writeAttribute("http://www.w3.org/2001/XMLSchema-instance","schemaLocation","http://pappso.inra.fr/xsd/masschroqml/2.2
  // http://pappso.inra.fr/xsd/masschroq-2.2.xsd");


  // writer.writeAttribute(xmlnsxsi, "noNamespaceSchemaLocation",
  // xsischemaLocation);

  _output_stream->writeAttribute("version", "1.0");

  _output_stream->writeAttribute("type", "MSidentificationResults");

  writeIdentMethod();
  _output_stream->writeStartElement("sequences");
  for(IdentificationGroup *p_ident_group :
      sp_project.get()->getIdentificationGroupList())
    {

      for(ProteinMatch *p_protein_match : p_ident_group->getProteinMatchList())
        {
          writeSequence(p_protein_match);
        }
    }
  //</sequences>
  _output_stream->writeEndElement();

  writeProject();
}

void
ProticdbMl::writeSequence(ProteinMatch *p_protein_match)
{
  qDebug() << "ProticdbMl::writeSequence begin";
  if(p_protein_match->getValidationState() != ValidationState::grouped)
    return;
  pappso::GrpProtein *p_grp_protein = p_protein_match->getGrpProteinSp().get();

  if(_map_accession2xmlid.find(p_grp_protein->getAccession()) ==
     _map_accession2xmlid.end())
    {
      // not found

      QString id = QString("seq%1").arg(_map_accession2xmlid.size());
      _output_stream->writeStartElement("sequence");
      _output_stream->writeAttribute("id", id);
      QString display_id =
        QString("%1 %2")
          .arg(p_grp_protein->getAccession())
          .arg(p_protein_match->getProteinXtpSp().get()->getDescription());
      _output_stream->writeAttribute("display_id", display_id.left(60));
      // <dbxref key="AT5G16390"
      // dbname="AGI_LocusCode"></dbxref>
      // if (prot.get_dbxref_type().equals("no") == false) {
      for(const DbXref &dbxref :
          p_protein_match->getProteinXtpSp().get()->getDbxrefList())
        {
          _output_stream->writeStartElement("dbxref");
          _output_stream->writeAttribute(
            "dbname", Utils::getDatabaseName(dbxref.database));
          _output_stream->writeAttribute("key", dbxref.accession);
          _output_stream->writeEndElement(); // dbxref
        }
      _output_stream->writeStartElement("description");
      _output_stream->writeCharacters(
        p_protein_match->getProteinXtpSp().get()->getDescription());
      _output_stream->writeEndElement(); // description
      _output_stream->writeStartElement("seq");
      _output_stream->writeCharacters(
        p_protein_match->getProteinXtpSp().get()->getOnlyAminoAcidSequence());
      _output_stream->writeEndElement(); // seq

      _output_stream->writeEndElement(); // sequence

      _map_accession2xmlid.insert(
        std::pair<QString, QString>(p_grp_protein->getAccession(), id));
    }
  qDebug() << "ProticdbMl::writeSequence end";
}

void
ProticdbMl::writeProject()
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeStartElement("project");
  _output_stream->writeAttribute("name", "");
  _output_stream->writeAttribute("id", "p1");
  // ajout des echantillons_msrun
  writeSamples();
  writeMsRuns();

  _output_stream->writeStartElement("identificationRuns");

  for(IdentificationGroup *p_identification :
      _sp_project.get()->getIdentificationGroupList())
    {
      _output_stream->writeStartElement("identificationRun");
      // ajout d'une mnip d'identification
      writeIdentificationRun(p_identification);

      // ajout des petides
      writepeptideHits(p_identification);

      // ajout des mathces
      writeMatchs(p_identification);

      _output_stream->writeEndElement(); // identificationRun
    }
  _output_stream->writeEndElement(); // identificationRuns
  _output_stream->writeEndElement(); // project

  _output_stream->writeEndDocument();

  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

void
ProticdbMl::writeIdentMethod()
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  // Ajout des mÃ©thodes
  _output_stream->writeStartElement("identMeth");
  _output_stream->writeAttribute("name", "");
  _output_stream->writeAttribute("id", "m1");
  _output_stream->writeEndElement();

  // Ajout des base de donnÃ©es
  _output_stream->writeStartElement("customDb");
  _output_stream->writeAttribute("name", "");
  _output_stream->writeAttribute("id", "customdb0");
  _output_stream->writeEndElement();
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

void
ProticdbMl::writeSamples()
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeStartElement("samples");
  for(MsRunSp msrun_sp : _sp_project.get()->getMsRunStore().getMsRunList())
    {
      _output_stream->writeStartElement("sample");
      QString id_samp =
        "bsamp" + pappso::Utils::getLexicalOrderedString(_sample_to_id.size());
      QString id_msrun = msrun_sp.get()->getXmlId();
      QString name     = msrun_sp.get()->getSampleName();
      // balise sample
      _output_stream->writeAttribute("name", name);
      _output_stream->writeAttribute("id", id_samp);
      _output_stream->writeEmptyElement("description");
      _sample_to_id.insert(std::pair<QString, QString>(name, id_samp));

      // Element spectrumList = doc.createElement("spectrumList");
      // msRun.appendChild(spectrumList);

      // msrun_to_id.put(samp.getSampleName(), id_msrun);
      _output_stream->writeEndElement(); // sample
    }
  _output_stream->writeEndElement(); // samples
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

void
ProticdbMl::writeMsRuns()
{

  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeStartElement("msRuns");
  for(MsRunSp msrun_sp : _sp_project.get()->getMsRunStore().getMsRunList())
    {

      QString name = msrun_sp.get()->getSampleName();

      QString id_samp = _sample_to_id.at(name);

      // Element spectrumList = doc.createElement("spectrumList");
      // msRun.appendChild(spectrumList);

      QString id_msrun = msrun_sp.get()->getXmlId();

      // balise MsRun
      _output_stream->writeStartElement("msRun");
      _output_stream->writeAttribute("sample_id", id_samp);
      _output_stream->writeAttribute("id", id_msrun);

      _output_stream->writeStartElement("description");
      _output_stream->writeStartElement("admin");
      // writer.writeStartElement("contact");
      // writer.writeStartElement("email");
      // writer.writeCharacters("valot@moulon.inra.fr");
      // writer.writeEndElement();// email

      // writer.writeStartElement("name");
      // writer.writeCharacters("Valot Benoit");
      // writer.writeEndElement();// name
      // writer.writeEndElement();// contact

      _output_stream->writeStartElement("sourceFile");
      _output_stream->writeStartElement("nameOfFile");
      _output_stream->writeCharacters(name + ".RAW");
      _output_stream->writeEndElement(); // nameOfFile
      _output_stream->writeEndElement(); // sourceFile

      _output_stream->writeStartElement("sampleName");
      _output_stream->writeCharacters(name);
      _output_stream->writeEndElement(); // sampleName
      _output_stream->writeEndElement(); // admin
      _output_stream->writeEndElement(); // description

      _output_stream->writeEndElement(); // msRun
    }
  _output_stream->writeEndElement(); // msRuns
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

void
ProticdbMl::writeIdentificationDataSource(
  IdentificationDataSource *p_identification_data_source)
{


  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeStartElement("dataProcessing");
  _output_stream->writeStartElement("software");
  _output_stream->writeStartElement("name");
  if(p_identification_data_source->getIdentificationEngine() ==
     IdentificationEngine::XTandem)
    {
      _output_stream->writeAttribute("acc", "PROTICdbO:0000283");
    }
  _output_stream->writeCharacters(
    p_identification_data_source->getIdentificationEngineName());

  _output_stream->writeEndElement(); // name
  _output_stream->writeStartElement("version");
  _output_stream->writeCharacters(
    p_identification_data_source->getIdentificationEngineVersion());
  _output_stream->writeEndElement(); // version
  _output_stream->writeEndElement(); // software
  _output_stream->writeStartElement("processingMethod");
  // mzXml source file name
  writeCvParam("PROTICdbO:0000343",
               p_identification_data_source->getMsRunSp().get()->getFileName(),
               "MS/MS data source file name");
  if(p_identification_data_source->getIdentificationEngine() ==
     IdentificationEngine::XTandem)
    {
      // model file name
      writeCvParam("PROTICdbO:0000342",
                   p_identification_data_source
                     ->getIdentificationEngineParam(
                       IdentificationEngineParam::tandem_param)
                     .toString(),
                   "X!tandem xml model file name");
      // xtandem result file name
      writeCvParam("PROTICdbO:0000341",
                   p_identification_data_source->getResourceName(),
                   "X!tandem xml result file name");
    }
  _output_stream->writeEndElement(); // processingMethod
  _output_stream->writeEndElement(); // dataProcessing
  /*
  _output_stream->writeStartElement("dataProcessing");
  _output_stream->writeStartElement("software");
  _output_stream->writeStartElement("name");
  _output_stream->writeAttribute("acc", "PROTICdbO:0000283");
  _output_stream->writeCharacters("X!Tandem");
  _output_stream->writeEndElement();// name
  _output_stream->writeStartElement("version");
  _output_stream->writeCharacters(XtandemPipelineSession.getInstance()
                         .getConfig().getXtandemVersion());
  _output_stream->writeEndElement();// version
  _output_stream->writeEndElement();// software
  _output_stream->writeStartElement("processingMethod");
  _output_stream->writeEndElement();// processingMethod
  _output_stream->writeEndElement();// dataProcessing
  */
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

void
ProticdbMl::writeIdentificationRun(IdentificationGroup *p_identification)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeAttribute("ident_method_id", "m1");
  _output_stream->writeAttribute("customdb_id", "customdb0");

  _output_stream->writeStartElement("description");
  _output_stream->writeStartElement("admin");
  // writer.writeStartElement("contact");
  // writer.writeStartElement("email");
  // writer.writeCharacters("valot@moulon.inra.fr");
  // writer.writeEndElement();// email

  // writer.writeStartElement("name");
  // writer.writeCharacters("Valot Benoit");
  // writer.writeEndElement();// name
  // writer.writeEndElement();// contact
  /*
  this.xpipFile = XtandemPipelineSession.getInstance()
  .getCurrentXpipFile();

  _output_stream->writeStartElement("sourceFile");
  _output_stream->writeStartElement("nameOfFile");
  // TODO
  // get the loaded xpip file name
  _output_stream->writeCharacters(this.xpipFile.getName());
  _output_stream->writeEndElement();// nameOfFile
  _output_stream->writeStartElement("pathToFile");
  _output_stream->writeCharacters(this.xpipFile.getAbsolutePath());
  _output_stream->writeEndElement();// pathToFile
  _output_stream->writeStartElement("fileType");
  _output_stream->writeCharacters("XPIP file");
  _output_stream->writeEndElement();// fileType
  _output_stream->writeEndElement();// sourceFile
  */
  _output_stream->writeEndElement(); // admin

  // if we can retrieve original information in xml xtandem results
  //
  for(IdentificationDataSource *p_identification_data_source :
      p_identification->getIdentificationDataSourceList())
    {
      writeIdentificationDataSource(p_identification_data_source);
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;

  // id: PROTICdbO:0000316
  // name: X!TandemPipeline

  _output_stream->writeStartElement("dataProcessing");
  _output_stream->writeStartElement("software");
  _output_stream->writeStartElement("name");
  _output_stream->writeAttribute("acc", "PROTICdbO:0000316");
  _output_stream->writeCharacters("X!TandemPipeline");
  _output_stream->writeEndElement(); // name
  _output_stream->writeStartElement("version");
  _output_stream->writeCharacters(XTPCPP_VERSION);
  _output_stream->writeEndElement(); // version
  _output_stream->writeEndElement(); // software
  _output_stream->writeStartElement("processingMethod");
  // cvParams

  writeCvParam("PROTICdbO:0000323",
               QString("%1").arg(_sp_project.get()
                                   ->getAutomaticFilterParameters()
                                   .getFilterProteinEvalue()),
               "X!TandemPipeline filter on protein evalue (log)");

  // indi
  // combine
  // phospho
  if(_sp_project.get()->getProjectMode() == ProjectMode::combined)
    {
      writeCvParam("PROTICdbO:0000320", "combine", "");
    }
  else
    {
      writeCvParam("PROTICdbO:0000319", "indiv", "");
    }
  // if (xtpExperimentType.equals("phospho")) {
  //    this.writeCvParam("PROTICdbO:0000321", xtpExperimentType, "");
  //}

  writeCvParam(
    "PROTICdbO:0000325",
    QString("%1").arg(_sp_project.get()
                        ->getAutomaticFilterParameters()
                        .getFilterMinimumPeptidePerMatch()),
    "X!TandemPipeline filter on minimal number of peptide per protein");

  writeCvParam("PROTICdbO:0000324",
               QString("%1").arg(_sp_project.get()
                                   ->getAutomaticFilterParameters()
                                   .getFilterPeptideEvalue()),
               "X!TandemPipeline filter on peptide evalue");

  // TODO write database filter
  // this.writeCvParam("PROTICdbO:0000324", ""
  // +
  // XtandemPipelineSession.getInstance().getConfig().get_database_filter(),
  // "X!TandemPipeline filter on peptide evalue");

  _output_stream->writeEndElement(); // processingMethod

  _output_stream->writeEndElement(); // dataProcessing

  _output_stream->writeEndElement(); // description
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}


void
ProticdbMl::writepeptideHits(IdentificationGroup *p_identification)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeStartElement("peptideHits");
  for(std::pair<unsigned int, GroupingGroupSp> group_pair :
      p_identification->getGroupStore().getGroupMap())
    {
      writepeptideHitsbyGroup(group_pair.second.get());
    }
  _output_stream->writeEndElement(); // "peptideHits");
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

QString
getProticPeptideHitKey(const PeptideEvidence *p_peptide_evidence)
{
  return (
    QString("%1 %2 %3")
      .arg(p_peptide_evidence->getHashSampleScan())
      .arg(p_peptide_evidence->getPeptideXtpSp().get()->toAbsoluteString())
      .arg(
        p_peptide_evidence->getIdentificationDataSource()->getResourceName()));
}

struct ProticPeptideHit
{
  ProticPeptideHit(QString ikey, PeptideMatch ipeptide_match)
    : key(ikey), peptide_match(ipeptide_match){};
  ProticPeptideHit(const ProticPeptideHit &other)
  {
    key           = other.key;
    peptide_match = other.peptide_match;
  };
  bool
  operator==(const ProticPeptideHit &other) const
  {
    return (key == other.key);
  };
  /** key = getProticPeptideHitKey
   * */
  QString key;
  PeptideMatch peptide_match;
};

void
ProticdbMl::writepeptideHitsbyGroup(GroupingGroup *p_group)
{

  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  std::vector<ProticPeptideHit> protic_peptide_hit_list;

  for(const ProteinMatch *p_protein_match : p_group->getProteinMatchList())
    {
      for(const PeptideMatch &peptide_match :
          p_protein_match->getPeptideMatchList(ValidationState::grouped))
        {
          ProticPeptideHit protic_peptide_hit = {
            getProticPeptideHitKey(peptide_match.getPeptideEvidence()),
            peptide_match};
          protic_peptide_hit_list.push_back(protic_peptide_hit);
        }
    }
  std::sort(protic_peptide_hit_list.begin(),
            protic_peptide_hit_list.end(),
            [](const ProticPeptideHit &first, const ProticPeptideHit &second) {
              return (first.key < second.key);
            });
  auto last =
    std::unique(protic_peptide_hit_list.begin(), protic_peptide_hit_list.end());
  protic_peptide_hit_list.erase(last, protic_peptide_hit_list.end());

  for(ProticPeptideHit &protic_peptide_hit : protic_peptide_hit_list)
    {
      QString xml_id = QString("pep%1").arg(_peptidekey_to_id.size() + 1);
      writePeptideHit(xml_id, protic_peptide_hit);
      _peptidekey_to_id.insert(
        std::pair<QString, QString>(protic_peptide_hit.key, xml_id));
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}


void
ProticdbMl::writePeptideHit(QString xml_id,
                            ProticPeptideHit &protic_peptide_hit)
{

  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  if(protic_peptide_hit.peptide_match.getPeptideEvidence() == nullptr)
    {
      throw pappso::PappsoException(
        QObject::tr(
          "Error protic_peptide_hit.peptide_match->getPeptideEvidence() == "
          "nullptr : \n%1 %2 %3")
          .arg(__FILE__)
          .arg(__FUNCTION__)
          .arg(__LINE__));
    }
  if(protic_peptide_hit.peptide_match.getPeptideEvidence()
       ->getPeptideXtpSp()
       .get() == nullptr)
    {
      throw pappso::PappsoException(
        QObject::tr("Error "
                    "protic_peptide_hit.peptide_match->getPeptideEvidence()->"
                    "getPeptideXtpSp().get() == nullptr : \n%1 %2 %3")
          .arg(__FILE__)
          .arg(__FUNCTION__)
          .arg(__LINE__));
    }
  // param par default
  _output_stream->writeStartElement("peptideHit");
  _output_stream->writeAttribute(
    "calc_mr",
    Utils::getXmlDouble(protic_peptide_hit.peptide_match.getPeptideEvidence()
                          ->getPeptideXtpSp()
                          .get()
                          ->getMass()));
  _output_stream->writeAttribute(
    "exp_mz",
    Utils::getXmlDouble(protic_peptide_hit.peptide_match.getPeptideEvidence()
                          ->getExperimentalMz()));
  _output_stream->writeAttribute(
    "delta",
    Utils::getXmlDouble(
      protic_peptide_hit.peptide_match.getPeptideEvidence()->getDeltaMass()));
  _output_stream->writeAttribute(
    "exp_mr",
    Utils::getXmlDouble(protic_peptide_hit.peptide_match.getPeptideEvidence()
                          ->getExperimentalMass()));
  _output_stream->writeAttribute(
    "acq_number",
    QString("%1").arg(
      protic_peptide_hit.peptide_match.getPeptideEvidence()->getScanNumber()));
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _output_stream->writeAttribute(
    "ms_run_id",
    protic_peptide_hit.peptide_match.getPeptideEvidence()
      ->getMsRunP()
      ->getXmlId());
  _output_stream->writeAttribute("id", xml_id);
  _output_stream->writeAttribute(
    "exp_z",
    QString("%1").arg(
      protic_peptide_hit.peptide_match.getPeptideEvidence()->getCharge()));

  writeCvParam("PROTICdbO:0000339",
               protic_peptide_hit.peptide_match.getPeptideEvidence()
                 ->getGrpPeptideSp()
                 .get()
                 ->getGroupingId(),
               "peptide mass id");
  /*
      if (xtpExperimentType.equals("phospho")) {
          this.writeCvParam("PROTICdbO:0000349",
                            Utils.getPappsoPhosphoPeptideMassId(group,
     peptideMass), "phosphopeptide mass id");
      }
  */
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  // pep.getMsRun().
  IdentificationEngine identification_engine =
    protic_peptide_hit.peptide_match.getPeptideEvidence()
      ->getIdentificationDataSource()
      ->getIdentificationEngine();
  if(identification_engine == IdentificationEngine::XTandem)
    {
      // cvparam specifique xtandem

      writeCvParam(
        "PROTICdbO:0000287",
        Utils::getXmlDouble(
          protic_peptide_hit.peptide_match.getPeptideEvidence()->getEvalue()),
        "xtandem peptide evalue");

      writeCvParam("PROTICdbO:0000288",
                   Utils::getXmlDouble(
                     protic_peptide_hit.peptide_match.getPeptideEvidence()
                       ->getParam(PeptideEvidenceParam::tandem_hyperscore)
                       .toDouble()),
                   "xtandem peptide hyperscore");
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  writeCvParam(
    "PROTICdbO:0000289",
    QString("%1").arg(protic_peptide_hit.peptide_match.getPeptideEvidence()
                        ->getRetentionTime()),
    "xtandem peptide retention time");

  // sequences avec les modifs
  _output_stream->writeStartElement("pepSeq");
  _output_stream->writeStartElement("peptide");
  _output_stream->writeCharacters(
    protic_peptide_hit.peptide_match.getPeptideEvidence()
      ->getPeptideXtpSp()
      .get()
      ->getSequence());
  _output_stream->writeEndElement(); // peptide
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  if(!protic_peptide_hit.peptide_match.getPeptideEvidence()
        ->getPeptideXtpSp()
        .get()
        ->getModifString()
        .isEmpty())
    {
      writePtms(protic_peptide_hit.peptide_match.getPeptideEvidence()
                  ->getPeptideXtpSp()
                  .get());
    }
  _output_stream->writeEndElement(); // pepSeq
  _output_stream->writeEndElement(); // peptideHit
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}


void
ProticdbMl::writePtms(PeptideXtp *p_peptide)
{
  _output_stream->writeStartElement("ptms");
  QStringList modif_list;
  unsigned int i = 1;
  for(const pappso::Aa &amino_acid : *p_peptide)
    {
      std::vector<pappso::AaModificationP> aa_modif_list =
        amino_acid.getModificationList();
      QStringList aamodif;
      for(auto &&aa_modif : aa_modif_list)
        {
          if(!aa_modif->isInternal())
            {
              aamodif << QString::number(aa_modif->getMass(), 'f', 2);
              _output_stream->writeStartElement("ptm");
              _output_stream->writeAttribute(
                "diff_mono", Utils::getXmlDouble(aa_modif->getMass()));
              _output_stream->writeAttribute("position", QString("%1").arg(i));
              _output_stream->writeAttribute("aa",
                                             QString(amino_acid.getLetter()));
              _output_stream->writeEndElement(); // ptm
            }
        }
      i++;
    }

  _output_stream->writeEndElement(); // ptms
}

void
ProticdbMl::writeMatchs(IdentificationGroup *p_identification)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;

  _output_stream->writeStartElement("matchs");
  for(std::pair<unsigned int, GroupingGroupSp> group_pair :
      p_identification->getGroupStore().getGroupMap())
    {
      // for (Group group : identification.getGrouping().getGroupList()) {
      std::vector<const ProteinMatch *> protein_match_list =
        group_pair.second.get()->getProteinMatchList();
      // sort protein match by subgroup
      std::sort(protein_match_list.begin(),
                protein_match_list.end(),
                [](const ProteinMatch *a, const ProteinMatch *b) {
                  return a->getGrpProteinSp().get()->getSubGroupNumber() <
                         b->getGrpProteinSp().get()->getSubGroupNumber();
                });

      std::vector<const ProteinMatch *> protein_match_subgroup_list;
      for(const ProteinMatch *p_protein_match : protein_match_list)
        {
          if(protein_match_subgroup_list.size() == 0)
            {
              protein_match_subgroup_list.push_back(p_protein_match);
            }
          else
            {
              if(protein_match_subgroup_list[0]
                   ->getGrpProteinSp()
                   .get()
                   ->getSubGroupNumber() !=
                 p_protein_match->getGrpProteinSp().get()->getSubGroupNumber())
                {
                  writeMatch(protein_match_subgroup_list);
                  protein_match_subgroup_list.clear();
                  protein_match_subgroup_list.push_back(p_protein_match);
                }
              else
                {
                  protein_match_subgroup_list.push_back(p_protein_match);
                }
            }
        }
      writeMatch(protein_match_subgroup_list);
    }
  _output_stream->writeEndElement(); // "matchs");
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

const QString &
ProticdbMl::getPeptideXmlId(const PeptideMatch *p_peptide_match) const
{
  QString peptide_key =
    getProticPeptideHitKey(p_peptide_match->getPeptideEvidence());
  auto it = _peptidekey_to_id.find(peptide_key);
  if(it == _peptidekey_to_id.end())
    {
      throw pappso::PappsoException(
        QObject::tr("Error peptide key %1 not found for peptide match %2 "
                    "sample=%3 scan=%4 start=%5")
          .arg(peptide_key)
          .arg(p_peptide_match->getPeptideEvidence()
                 ->getPeptideXtpSp()
                 .get()
                 ->toString())
          .arg(p_peptide_match->getPeptideEvidence()->getMsRunP()->getXmlId())
          .arg(p_peptide_match->getPeptideEvidence()->getScanNumber())
          .arg(p_peptide_match->getStart()));
    }
  return it->second;
}
const QString &
ProticdbMl::getProteinXmlId(const ProteinMatch *p_protein_match) const
{
  auto it = _map_accession2xmlid.find(
    p_protein_match->getProteinXtpSp().get()->getAccession());
  if(it == _map_accession2xmlid.end())
    {
      throw pappso::PappsoException(
        QObject::tr("Error protein accession %1 not found for protein %2")
          .arg(p_protein_match->getProteinXtpSp().get()->getAccession())
          .arg(p_protein_match->getProteinXtpSp().get()->getAccession()));
    }
  return it->second;
}
void
ProticdbMl::writeMatch(std::vector<const ProteinMatch *> &protein_match_sg_list)
{
  try
    {
      if(protein_match_sg_list.size() == 0)
        return;
      // for (SubGroup sg : group.getSubGroupSet().getSubGroupList()) {
      _output_stream->writeStartElement("match");

      // samples
      QStringList sample_name_list;
      for(const PeptideMatch &peptide_match :
          protein_match_sg_list[0]->getPeptideMatchList(
            ValidationState::grouped))
        {
          sample_name_list
            << peptide_match.getPeptideEvidence()->getMsRunP()->getSampleName();
        }
      sample_name_list = sample_name_list.toSet().values();
      for(QString sample_name : sample_name_list)
        {
          _output_stream->writeStartElement("matchSample");
          _output_stream->writeAttribute("sample_id",
                                         _sample_to_id[sample_name]);
          _output_stream->writeAttribute("name", sample_name);
          _output_stream->writeEndElement(); // "matchSample");
        }

      // proteins
      for(const ProteinMatch *p_protein_match : protein_match_sg_list)
        {
          _output_stream->writeStartElement("proteinHit");
          _output_stream->writeAttribute(
            "sequence_id",
            _map_accession2xmlid.at(
              p_protein_match->getProteinXtpSp().get()->getAccession()));
          // proteinHit.setAttribute("score", "");
          _output_stream->writeAttribute(
            "rank",
            QString("%1").arg(
              p_protein_match->getGrpProteinSp().get()->getRank()));

          // //cvparam
          writeCvParam("PROTICdbO:0000284",
                       QString("%1").arg(
                         p_protein_match->getProteinXtpSp().get()->getMass()),
                       "MW computation");
          // evalue
          writeCvParam("PROTICdbO:0000291",
                       Utils::getXmlDouble(p_protein_match->getLogEvalue()),
                       "Xtandem log evalue");

          // coverage
          writeCvParam("PROTICdbO:0000285",
                       Utils::getXmlDouble(p_protein_match->getCoverage()),
                       "protein coverage");

          // [Term]
          // id: PROTICdbO:0000335
          // name: X!TandemPipeline PAI
          writeCvParam("PROTICdbO:0000335",
                       Utils::getXmlDouble(p_protein_match->getPAI()),
                       "PAI");

          // [Term]
          // id: PROTICdbO:0000337
          // name: protein group number
          writeCvParam(
            "PROTICdbO:0000337",
            p_protein_match->getGrpProteinSp().get()->getGroupingId(),
            "grouping number");

          _output_stream->writeEndElement(); // "proteinHit");
        }

      for(const ProteinMatch *p_protein_match : protein_match_sg_list)
        {
          // peptides
          for(const PeptideMatch &peptide_match :
              p_protein_match->getPeptideMatchList(ValidationState::grouped))
            {
              // peptidesHitRef
              QString peptide_xml_id = getPeptideXmlId(&peptide_match);

              _output_stream->writeStartElement("peptideHitRef");
              _output_stream->writeAttribute("peptide_hit_id", peptide_xml_id);

              _output_stream->writeStartElement("fromSeq");
              _output_stream->writeAttribute("seq_id",
                                             getProteinXmlId(p_protein_match));
              _output_stream->writeAttribute(
                "start", QString("%1").arg(peptide_match.getStart() + 1));
              _output_stream->writeAttribute(
                "stop", QString("%1").arg(peptide_match.getStop() + 1));
              QString residue_before_nter = "-";
              if(peptide_match.getStart() > 0)
                {
                  residue_before_nter =
                    p_protein_match->getProteinXtpSp().get()->getSequence().mid(
                      peptide_match.getStart() - 1, 1);
                }
              _output_stream->writeAttribute("residue_before_nter",
                                             residue_before_nter);
              QString residue_after_cter = "-";
              if((int)peptide_match.getStop() <
                 (p_protein_match->getProteinXtpSp()
                    .get()
                    ->getSequence()
                    .size() -
                  1))
                {
                  residue_after_cter =
                    p_protein_match->getProteinXtpSp().get()->getSequence().mid(
                      peptide_match.getStop(), 1);
                }
              _output_stream->writeAttribute("residue_after_cter",
                                             residue_after_cter);

              _output_stream->writeEndElement(); // fromSeq
              _output_stream->writeEndElement(); // peptideHitRef
            }
        }
      _output_stream->writeEndElement(); // "match");
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error in ProticdbMl::writeMatch :\n%1")
          .arg(error.qwhat()));
    }
  catch(std::exception &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error in ProticdbMl::writeMatch stdex :\n%1")
          .arg(error.what()));
    }
}
void
ProticdbMl::writeOboModif(pappso::AaModificationP mod)
{
  _output_stream->writeStartElement("cvParam");
  _output_stream->writeAttribute("name", mod->getName());
  _output_stream->writeAttribute("cvLabel", "MOD");
  _output_stream->writeAttribute("accession", mod->getAccession());
  _output_stream->writeEndElement(); // cvParam
}

void
ProticdbMl::writeCvParam(QString acc, QString value, QString description)
{
  _output_stream->writeStartElement("cvParam");

  _output_stream->writeAttribute("value", value);
  if(description.isEmpty())
    {
      _output_stream->writeAttribute("name", "N.A.");
    }
  else
    {
      _output_stream->writeAttribute("name", description);
    }
  _output_stream->writeAttribute("cvLabel", "PROTICdbO");
  _output_stream->writeAttribute("accession", acc);
  _output_stream->writeEndElement(); // cvParam
}
