/**
 * \file processing/xml/mzidentmlwriter.cpp
 * \date 07/02/2024
 * \author Olivier Langella
 * \brief MzIdentML writer
 */

/*******************************************************************************
 * Copyright (c) 2024 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of PAPPSOms-tools.
 *
 *     PAPPSOms-tools is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PAPPSOms-tools is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "mzidentmlwriter.h"
#include <QDateTime>
#include "../../pappsoexception.h"

pappso::MzIdentMlWriter::MzIdentMlWriter()
{
}

pappso::MzIdentMlWriter::~MzIdentMlWriter()
{
}

void
pappso::MzIdentMlWriter::writeStartMzIdentMlDocument(const QString &id,
                                                     const QString &version)
{
  setAutoFormatting(true);
  writeStartDocument("1.0");
  // <MzIdentML id="X! Tandem" version="1.1.0"
  // xmlns="http://psidev.info/psi/pi/mzIdentML/1.1"
  // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  // xsi:schemaLocation="http://psidev.info/psi/pi/mzIdentML/1.1
  // http://www.psidev.info/files/mzIdentML1.1.0.xsd"
  // creationDate="2016:10:06:09:43:19" >


  writeDefaultNamespace("http://psidev.info/psi/pi/mzIdentML/1.2");
  writeStartElement("MzIdentML");
  writeAttribute("id", id);
  writeAttribute("version", version);
  writeAttribute("creationDate",
                 QDateTime::currentDateTime().toString(Qt::ISODate));
  writeNamespace("http://www.w3.org/2001/XMLSchema-instance", "xsi");
  // mp_xmlWriter->writeNamespace("http://www.w3.org/2001/XMLSchema-instance","xsi");
  // xsi:schemaLocation="http://psidev.info/psi/pi/mzIdentML/1.1
  // http://www.psidev.info/files/mzIdentML1.1.0.xsd"
  // xmlns:PSI-MS="http://psidev.info/psi/pi/mzIdentML/1.0"
  writeAttribute(
    "http://www.w3.org/2001/XMLSchema-instance",
    "schemaLocation",
    "http://psidev.info/psi/pi/mzIdentML/1.2 "
    "https://raw.githubusercontent.com/HUPO-PSI/mzIdentML/master/schema/"
    "mzIdentML1.2.0-candidate.xsd");
}

void
pappso::MzIdentMlWriter::writeCvList()
{

  writeStartElement("cvList");
  // m_ofOut << "<cvList xmlns=\"http://psidev.info/psi/pi/mzIdentML/1.1\">\n";
  writeStartElement("cv");
  writeAttribute("id", "PSI-MS");
  writeAttribute(
    "uri",
    "http://psidev.cvs.sourceforge.net/viewvc/*checkout*/psidev/psi/psi-ms/"
    "mzML/controlledVocabulary/psi-ms.obo");
  writeAttribute("version", "3.30.0");
  writeAttribute("fullName", "PSI-MS");
  writeEndElement();
  // m_ofOut << "    <cv id=\"PSI-MS\" "
  //          "uri=\"http://psidev.cvs.sourceforge.net/viewvc/*checkout*/psidev/"
  //        "psi/psi-ms/mzML/controlledVocabulary/psi-ms.obo\" "
  //       "version=\"3.30.0\" fullName=\"PSI-MS\"/>\n";

  writeStartElement("cv");
  writeAttribute("id", "PSI-MOD");
  writeAttribute(
    "uri", "http://psidev.cvs.sourceforge.net/psidev/psi/mod/data/PSI-MOD.obo");
  writeAttribute(
    "fullName",
    "Proteomics Standards Initiative Protein Modifications Vocabularies");
  writeAttribute("version", "1.2");
  writeEndElement();

  // m_ofOut
  // << "    <cv id=\"UNIMOD\" uri=\"http://www.unimod.org/obo/unimod.obo\" "
  //  "fullName=\"UNIMOD\"/>\n";

  writeStartElement("cv");
  writeAttribute("id", "UO");
  writeAttribute("uri",
                 "http://obo.cvs.sourceforge.net/*checkout*/"
                 "obo/obo/ontology/phenotype/unit.obo");
  writeAttribute("fullName", "UNIT-ONTOLOGY");
  writeEndElement();
  // m_ofOut << "    <cv id=\"UO\" "
  //          "uri=\"http://obo.cvs.sourceforge.net/*checkout*/obo/obo/ontology/"
  //        "phenotype/unit.obo\" fullName=\"UNIT-ONTOLOGY\"/>\n";
  writeEndElement();
  // m_ofOut << "</cvList>\n";
}

void
pappso::MzIdentMlWriter::writeDBSequence(const QString &id,
                                         const QString &db_ref_id,
                                         const pappso::Protein &protein,
                                         bool is_reverse)
{
  writeDBSequence(id,
                  db_ref_id,
                  protein.getAccession(),
                  protein.getDescription(),
                  protein.getSequence(),
                  is_reverse);
}


void
pappso::MzIdentMlWriter::writeDBSequence(const QString &id,
                                         const QString &db_ref_id,
                                         const QString &accession,
                                         const QString &description,
                                         const QString &sequence,
                                         bool is_reverse)
{

  writeStartElement("DBSequence");
  writeAttribute("id", id);
  writeAttribute("searchDatabase_ref", db_ref_id);
  writeAttribute("accession", accession);
  writeAttribute("length", QString::number(sequence.size()));
  //  m_ofOut << "<DBSequence accession=\"" << strLabel.c_str()
  //        << "\" searchDatabase_ref=\"SearchDB_"
  //      << _vs[a].m_vseqBest[b].m_siPath << "\" length=\""
  //    << (unsigned long)_vs[a].m_vseqBest[b].m_strSeq.size()
  //  << "\" id=\"DBSeq" << tUid << "\">\n";


  writeStartElement("Seq");
  writeCharacters(sequence);
  writeEndElement();


  if(!description.isEmpty())
    {
      writeCvParam("PSI-MS", "MS:1001088", "protein description", description);
    }
  //  m_ofOut << "<cvParam accession=\"MS:1001088\" cvRef=\"PSI-MS\"
  //  "
  //           "value=\""
  //      << strDesc.c_str()
  //    << "\" name=\"protein description\"/>\n";

  if(is_reverse)
    {
      // PSI-MS MS:1001195 decoy DB type reverse
      writeCvParam("PSI-MS", "MS:1001195", "decoy DB type reverse", "");
    }
  writeEndElement();
  // m_ofOut << "</DBSequence>\n";
}


void
pappso::MzIdentMlWriter::writeCvParam(const QString &cv_ref,
                                      const QString &accession,
                                      const QString &name,
                                      const QString &value)
{

  //<cvParam accession="MS:1001083" cvRef="PSI-MS" name="ms-ms search"/>
  writeStartElement("cvParam");
  writeAttribute("accession", accession);
  writeAttribute("cvRef", cv_ref);
  writeAttribute("name", name);
  if(!value.isEmpty())
    writeAttribute("value", value);
  writeEndElement();
}

void
pappso::MzIdentMlWriter::writeCvParam(const pappso::OboPsiModTerm &term,
                                      const QString &value)
{
  if(term.m_accession.startsWith("MOD:"))
    {
      writeCvParam("PSI-MOD", term.m_accession, term.m_name, value);
    }
  else
    {
      if(term.m_accession.startsWith("MS:"))
        {
          writeCvParam("PSI-MS", term.m_accession, term.m_name, value);
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr("The OBO term %1 %2 is not an OBO PSI-MOD term in %3")
              .arg(term.m_accession)
              .arg(term.m_name)
              .arg(__FUNCTION__));
        }
    }
}

void
pappso::MzIdentMlWriter::writeCvParam(pappso::AaModificationP pmod)
{
  if(pmod == nullptr)
    {
      throw pappso::PappsoException(
        QObject::tr("pappso::AaModificationP is null in %1").arg(__FUNCTION__));
    }
  else
    {
      if(pmod->getAccession().startsWith("MOD:"))
        {
          writeCvParam("PSI-MOD", pmod->getAccession(), pmod->getName(), "");
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr(
              "The modification %1 %2 is not an OBO PSI-MOD term in %3")
              .arg(pmod->getAccession())
              .arg(pmod->getName())
              .arg(__FUNCTION__));
        }
    }
}


///< cvParam accession="MS:1000894" cvRef="PSI-MS" name="retention time"
///< value="5468.0193" unitAccession="UO:0000010" unitName="second"
///< unitCvRef="UO"/>
void
pappso::MzIdentMlWriter::writeCvParamUo(const QString &cv_ref,
                                        const QString &accession,
                                        const QString &name,
                                        const QString &value,
                                        const QString &unit_cv_ref,
                                        const QString &unit_accession,
                                        const QString &unit_name)
{
  //<cvParam accession="MS:1001083" cvRef="PSI-MS" name="ms-ms search"/>
  writeStartElement("cvParam");
  writeAttribute("accession", accession);
  writeAttribute("cvRef", cv_ref);
  writeAttribute("name", name);
  if(!value.isEmpty())
    writeAttribute("value", value);
  writeAttribute("unitAccession", unit_accession);
  writeAttribute("unitName", unit_name);
  writeAttribute("unitCvRef", unit_cv_ref);
  writeEndElement();
}

void
pappso::MzIdentMlWriter::writeCvParamUo(const QString &cv_ref,
                                        const QString &accession,
                                        const QString &name,
                                        const QString &value,
                                        pappso::PrecisionUnit precision_unit)
{
  switch(precision_unit)
    {
      case pappso::PrecisionUnit::dalton:
        writeCvParamUo(
          cv_ref, accession, name, value, "UO", "UO:0000221", "dalton");
        break;
      case pappso::PrecisionUnit::ppm:
        writeCvParamUo(cv_ref,
                       accession,
                       name,
                       value,
                       "UO",
                       "UO:0000169",
                       "parts per million");
        break;
      default:
        throw pappso::PappsoException(
          QObject::tr("precision unit %1 not implemented in %2")
            .arg((int)precision_unit)
            .arg(__FUNCTION__));
        break;
    }
}


void
pappso::MzIdentMlWriter::writeUserParam(const QString &name,
                                        const QString &value)
{
  writeStartElement("userParam");
  writeAttribute("name", name);
  writeAttribute("value", value);
  writeEndElement();
}


void
pappso::MzIdentMlWriter::writeUserParam(pappso::DeepProtMatchType matchType)
{
  writeUserParam("DeepProt:match_type",
                 pappso::DeepProtEnumStr::toString(matchType));
}

void
pappso::MzIdentMlWriter::writeUserParam(
  pappso::DeepProtPeptideCandidateStatus status)
{
  writeUserParam("DeepProt:status", pappso::DeepProtEnumStr::toString(status));
}

const QString
pappso::MzIdentMlWriter::toXmlMass(double mass)
{
  return QString::number(mass, 'f', 10);
}

const QString
pappso::MzIdentMlWriter::toXmlMassDelta(double mass)
{
  return QString::number(mass, 'g', 10);
}

void
pappso::MzIdentMlWriter::writeUserParam(const QString &name, double value)
{
  writeUserParam(name, QString::number(value, 'g', 10));
}

const QString
pappso::MzIdentMlWriter::toXmlDouble(double value)
{
  return QString::number(value, 'g', 10);
}
