
/**
 * \file mcql/input/masschroqml2jsonparser.cpp
 * \date 29/12/2024
 * \author Olivier Langella
 * \brief masschroqML parser conversion 2 json for MassChroqLight
 */

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

#include "masschroqml2jsonparser.h"
#include <QFileInfo>
#include <QDir>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/exception/exceptionnotrecognized.h>
#include <pappsomspp/core/processing/filters/filtermorpho.h>
#include <pappsomspp/core/processing/filters/filterpass.h>
#include <QJsonArray>
#include <QJsonDocument>


mcql::MassChroqMl2JsonParser::MassChroqMl2JsonParser()
{
}

mcql::MassChroqMl2JsonParser::~MassChroqMl2JsonParser()
{
}

bool
mcql::MassChroqMl2JsonParser::readFile(const QString &fileName)
{

  QDir::setCurrent(QFileInfo(fileName).absolutePath());
  return pappso::XmlStreamReaderInterface::readFile(fileName);
}

void
mcql::MassChroqMl2JsonParser::readStream()
{
  try
    {
      if(m_qxmlStreamReader.readNextStartElement())
        {
          if(m_qxmlStreamReader.name().toString() == "masschroq")
            {

              while(m_qxmlStreamReader.readNextStartElement())
                {
                  qDebug() << m_qxmlStreamReader.name();
                  if(m_qxmlStreamReader.name().toString() ==
                     "project_parameters")
                    readProjectParameters();
                  else if(m_qxmlStreamReader.name().toString() == "rawdata")
                    readRawdata();
                  // m_qxmlStreamReader.skipCurrentElement();
                  else if(m_qxmlStreamReader.name().toString() == "groups")
                    readGroups();
                  else if(m_qxmlStreamReader.name().toString() ==
                          "protein_list")
                    readProteinList();
                  // m_qxmlStreamReader.skipCurrentElement();
                  else if(m_qxmlStreamReader.name().toString() ==
                          "peptide_list")
                    readPeptideList();
                  else if(m_qxmlStreamReader.name().toString() == "alignments")
                    readAlignments();
                  // m_qxmlStreamReader.skipCurrentElement();
                  else if(m_qxmlStreamReader.name().toString() ==
                          "quantification_methods")
                    readQuantificationMethods();
                  // m_qxmlStreamReader.skipCurrentElement();
                  else if(m_qxmlStreamReader.name().toString() ==
                          "quantification")
                    // readQuantification();
                    m_qxmlStreamReader.skipCurrentElement();
                  else if(m_qxmlStreamReader.name().toString() ==
                          "isotope_label_list")
                    // readQuantification();
                    m_qxmlStreamReader.skipCurrentElement();
                  else
                    {


                      m_qxmlStreamReader.raiseError(
                        QObject::tr(
                          "ERROR parsing XML file at tag %1:\n%2 unknown")
                          .arg(m_qxmlStreamReader.name()));
                    }
                }
            }
        }
    }

  catch(const pappso::PappsoException &e)
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("ERROR parsing XML file at tag %1:\n%2")
          .arg(m_qxmlStreamReader.name())
          .arg(e.qwhat()));
    }
}

void
mcql::MassChroqMl2JsonParser::readProjectParameters()
{
  //<project_param category="1" name="i2MassChroQ_VERSION" value="1.0.9"/>

  while(m_qxmlStreamReader.readNextStartElement())
    {
      if(m_qxmlStreamReader.name().toString() == "project_param")
        {
          QJsonObject param;
          param.insert(
            "category",
            QJsonValue(
              m_qxmlStreamReader.attributes().value("category").toInt()));
          bool is_ok = false;
          int ival =
            m_qxmlStreamReader.attributes().value("value").toInt(&is_ok);
          if(is_ok)
            param.insert("value", ival);
          else
            {
              double dval =
                m_qxmlStreamReader.attributes().value("value").toDouble(&is_ok);
              if(is_ok)
                param.insert("value", dval);

              else
                {
                  QString sval =
                    m_qxmlStreamReader.attributes().value("value").toString();
                  param.insert("value", sval);
                }
            }

          m_jsonProjectParameters.insert(
            m_qxmlStreamReader.attributes().value("name").toString(), param);
          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("ERROR parsing XML file at tag %1:\n%2 unknown")
              .arg(m_qxmlStreamReader.name()));
        }
    }
}


void
mcql::MassChroqMl2JsonParser::readAlignments()
{
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      if(m_qxmlStreamReader.name().toString() == "alignment_methods")
        {
          readAlignmentMethods();
        }

      else if(m_qxmlStreamReader.name().toString() == "align")
        {
          read_align();
        }
      else
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("ERROR parsing XML file at tag alignments:\n%1 unknown")
              .arg(m_qxmlStreamReader.name()));
        }
    }
}


void
mcql::MassChroqMl2JsonParser::readAlignmentMethods()
{
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      if(m_qxmlStreamReader.name().toString() == "alignment_method")
        {
          read_alignment_method();
        }
    }
}


/// <alignment_method id="obiwarp1">
void
mcql::MassChroqMl2JsonParser::read_alignment_method()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "alignment_method")
    {
      try
        {
          QString alignment_method_id =
            m_qxmlStreamReader.attributes().value("id").toString();
          if(alignment_method_id.isEmpty())
            {
              m_qxmlStreamReader.raiseError(QObject::tr(
                "the alignment_method tag must have an id attribute."));
            }

          /// we will set the current_alignment_method later (in the <obiwarp or
          /// <ms2> tag that follows this one )
          //  _p_current_alignment_method = NULL;

          if(m_qxmlStreamReader.readNextStartElement())
            {
              qDebug() << m_qxmlStreamReader.name();
              if(m_qxmlStreamReader.name().toString() == "obiwarp")
                {
                  m_qxmlStreamReader.raiseError(
                    QObject::tr("sorry, obiwarp alignment method is no more "
                                "available since MassChroQ "
                                "version 2.2.23"));
                }
              else if(m_qxmlStreamReader.name().toString() == "ms2")
                {
                  read_ms2();
                }
            }
        }

      catch(pappso::PappsoException &error)
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("error reading %1 tag :\n%2")
              .arg(m_qxmlStreamReader.name().toString())
              .arg(error.qwhat()));
        }
      qDebug() << m_qxmlStreamReader.name();
      m_qxmlStreamReader.skipCurrentElement();
      qDebug() << m_qxmlStreamReader.name();
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("Not an alignment_method element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}

/// <ms2>

void
mcql::MassChroqMl2JsonParser::read_ms2()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "ms2")
    {

      QString time_output_dir = m_qxmlStreamReader.attributes()
                                  .value("write_time_values_output_dir")
                                  .toString();
      if(!time_output_dir.isEmpty())
        {
          // Print time value
        }
      else
        {
          // not print time value
        }

      while(m_qxmlStreamReader.readNextStartElement())
        {
          qDebug() << m_qxmlStreamReader.name();
          QString inside = m_qxmlStreamReader.readElementText();
          qDebug() << m_qxmlStreamReader.name();
          /// <ms2_tendency_halfwindow>10</ms2_tendency_halfwindow>
          if(m_qxmlStreamReader.name().toString() == "ms2_tendency_halfwindow")
            {
              m_jsonAlignmentMethod.insert("ms2_tendency", inside.toDouble());
            }

          /// <ms2_smoothing_halfwindow>5</ms2_smoothing_halfwindow>

          else if(m_qxmlStreamReader.name().toString() ==
                  "ms2_smoothing_halfwindow")
            {
              m_jsonAlignmentMethod.insert("ms2_smoothing", inside.toDouble());
            }

          /// <ms1_smoothing_halfwindow>15</ms1_smoothing_halfwindow>
          else if(m_qxmlStreamReader.name().toString() ==
                  "ms1_smoothing_halfwindow")
            {
              m_jsonAlignmentMethod.insert("ms1_smoothing", inside.toDouble());
            }
        }
    }

  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not an ms2 element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


/// <align group_id="G1" method_id="obiwarp1">
void
mcql::MassChroqMl2JsonParser::read_align()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "align")
    {
      QString group_id =
        m_qxmlStreamReader.attributes().value("group_id").toString();
      QString align_method_id =
        m_qxmlStreamReader.attributes().value("method_id").toString();
      QString ref_msrun_id =
        m_qxmlStreamReader.attributes().value("reference_data_id").toString();

      if(group_id.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the align tag must have a group_id attribute."));
        }
      if(align_method_id.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the align tag must have a method_id attribute."));
        }
      if(ref_msrun_id.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the align tag must have a ref_msrun_id attribute."));
        }

      QJsonObject qgroup;
      qgroup.insert("alignment_reference", ref_msrun_id);
      m_jsonQuantifyGroup.insert(group_id, qgroup);
      /// launch alignment
      /*
      mp_massChroq->alignGroup(
        m_uiMonitor, group_id, align_method_id, ref_msrun_id);
        */
      m_qxmlStreamReader.skipCurrentElement();
    }

  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not an align element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::readProteinList()
{
  qDebug();
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "protein_list")
    {

      while(m_qxmlStreamReader.readNextStartElement())
        {
          qDebug() << m_qxmlStreamReader.name();
          //<protein desc="conta|P02769|ALBU_BOVIN SERUM ALBUMIN PRECURSOR."
          // id="P1.1" />
          read_protein();
        }
    }
  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not a protein_list element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::read_protein()
{

  //<protein desc="conta|P02769|ALBU_BOVIN SERUM ALBUMIN PRECURSOR."
  // id="P1.1" />
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "protein")
    {
      QString id   = m_qxmlStreamReader.attributes().value("id").toString();
      QString desc = m_qxmlStreamReader.attributes().value("desc").toString();
      if(id.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the protein tag must have an id attribute."));
        }
      QJsonObject protein;
      protein.insert("description", desc);
      m_jsonProteinList.insert(id, protein);

      qDebug();
      m_qxmlStreamReader.skipCurrentElement();
    }

  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not a protein element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
  qDebug() << m_qxmlStreamReader.name();
}


void
mcql::MassChroqMl2JsonParser::readRawdata()
{
  std::map<QString, QString> msfilepathlist;
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      //<data_file id="samp0" format="mzxml" path="bsa1.mzXML"
      // type="profile" />

      if(m_qxmlStreamReader.name().toString() == "data_file")
        {
          QString filename =
            m_qxmlStreamReader.attributes().value("path").toString();
          if(filename.isEmpty())
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("The data_file tag must have a path attribute."));
            }

          QString idname =
            m_qxmlStreamReader.attributes().value("id").toString();
          if(idname.isEmpty())
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("The data_file tag must have an id attribute."));
            }

          QFileInfo filenameInfo(filename);
          if(!filenameInfo.exists())
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("cannot find input file : %2 \n").arg(filename));
            }
          else if(!filenameInfo.isReadable())
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("cannot read input file : %2 \n").arg(filename));
            }

          QJsonObject msrun;

          msrun.insert("file", filenameInfo.absoluteFilePath());


          m_jsonMsRunList.insert(idname, msrun);

          m_qxmlStreamReader.skipCurrentElement();
        }

      else
        {
          m_qxmlStreamReader.raiseError(QObject::tr("Not a data_file element"));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}


void
mcql::MassChroqMl2JsonParser::readPeptideList()
{
  qDebug();
  m_msrunPeptideJsonObservationMap.clear();
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      read_peptide();
    }


  //"msruna1": {
  //"peptide_obs": { "pepa1a1":
  //  [{

  for(auto &itmsrun_pair : m_msrunPeptideJsonObservationMap)
    {
      QJsonObject peptide_list;
      QString msrun = itmsrun_pair.first;
      for(auto &itpeptide_pair : itmsrun_pair.second)
        {
          peptide_list.insert(itpeptide_pair.first, itpeptide_pair.second);
        }
      QJsonObject peptide_obs;
      peptide_obs.insert("peptide_obs", peptide_list);
      m_jsonMsRunPeptideList.insert(msrun, peptide_obs);
    }
  m_msrunPeptideJsonObservationMap.clear();
}


void
mcql::MassChroqMl2JsonParser::read_peptide()
{

  qDebug() << m_qxmlStreamReader.name();
  QStringList msrun_id_list;
  if(m_qxmlStreamReader.name().toString() == "peptide")
    {
      QString idname = m_qxmlStreamReader.attributes().value("id").toString();
      QString mh     = m_qxmlStreamReader.attributes().value("mh").toString();
      QString seq    = m_qxmlStreamReader.attributes().value("seq").toString();
      QString mods   = m_qxmlStreamReader.attributes().value("mods").toString();

      QJsonObject json_peptide;

      // prot_ids
      QStringList st_prot_list =
        m_qxmlStreamReader.attributes()
          .value("prot_ids")
          .toString()
          .split(" ", Qt::SplitBehaviorFlags::SkipEmptyParts);
      if(st_prot_list.size() == 0)
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the peptide tag must have a prot_ids attribute."));
        }
      else
        {
          QJsonArray protein_array;
          for(const QString &prot_id : st_prot_list)
            {
              protein_array.append(prot_id);
            }

          json_peptide.insert("proteins", protein_array);
        }
      if(idname.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the peptide tag must have an id attribute."));
        }
      if(mh.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the peptide tag must have a mh attribute."));
        }
      /// create a new Peptide object and set its members
      if(seq.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the peptide tag must have a seq attribute."));
        }
      else
        {
          try
            {
              pappso::Peptide peptide(seq);

              // modifications + observed_in
              while(m_qxmlStreamReader.readNextStartElement())
                {
                  qDebug() << m_qxmlStreamReader.name();
                  if(m_qxmlStreamReader.name().toString() == "modifications")
                    {
                      while(m_qxmlStreamReader.readNextStartElement())
                        {
                          if(m_qxmlStreamReader.name().toString() == "psimod")
                            {
                              read_psimod(peptide);
                            }
                          else
                            {
                              m_qxmlStreamReader.raiseError(
                                QObject::tr("%1 element not allowed here")
                                  .arg(m_qxmlStreamReader.name()));
                              m_qxmlStreamReader.skipCurrentElement();
                            }
                        }
                    }
                  else if(m_qxmlStreamReader.name().toString() == "observed_in")
                    {
                      json_peptide.insert("proforma", peptide.toProForma());
                      json_peptide.insert("mods", mods);

                      m_jsonPeptideList.insert(idname, json_peptide);
                      /*
                      if(peptide_sp.get() == nullptr)
                        {
                          peptide_sp = std::make_shared<mcql::Peptide>(
                            idname, peptide.makePeptideSp(), protein_list);
                          m_peptideStore.push_back(peptide_sp);
                          peptide_sp.get()->setMods(mods);
                        }
*/
                      read_observed_in(idname);

                      // m_qxmlStreamReader.skipCurrentElement();
                    }
                  else
                    {
                      m_qxmlStreamReader.raiseError(
                        QObject::tr("%1 element not allowed here")
                          .arg(m_qxmlStreamReader.name()));
                      m_qxmlStreamReader.skipCurrentElement();
                    }
                }
            }
          catch(const pappso::PappsoException &e)
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("error building peptide %1\n%2")
                  .arg(seq)
                  .arg(e.qwhat()));
            }
        }
    }
  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not a peptide element"));
      m_qxmlStreamReader.skipCurrentElement();
    }

  qDebug() << m_qxmlStreamReader.name();
}


/// <observed_in data="delumeau1" scan="33" z="2">
void
mcql::MassChroqMl2JsonParser::read_observed_in(const QString &peptide_id)
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "observed_in")
    {
      QJsonObject observation;
      QString msrun = m_qxmlStreamReader.attributes().value("data").toString();
      QString scan  = m_qxmlStreamReader.attributes().value("scan").toString();
      QString index = m_qxmlStreamReader.attributes().value("index").toString();
      QString z     = m_qxmlStreamReader.attributes().value("z").toString();
      if(msrun.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the observed_in tag must have a data attribute "
                        "(reference on a valid msRun id)."));
        }
      if(scan.isEmpty() && index.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the observed_in tag must have an index attribute."));
        }

      if(scan.isEmpty())
        observation.insert("index", index.toLongLong());
      if(index.isEmpty())
        observation.insert("scan", scan.toLongLong());

      if(z.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the observed_in tag must have a z attribute."));
        }


      QJsonObject precursor;

      precursor.insert("charge", z.toInt());

      observation.insert("precursor", precursor);

      /// add the observed in infos to the current msunpeptide observed
      /// peptide
      auto itmsrun = m_msrunPeptideJsonObservationMap.insert(
        {msrun, std::map<QString, QJsonArray>()});
      auto itpeparr = itmsrun.first->second.insert({peptide_id, QJsonArray()});
      itpeparr.first->second.append(observation);
      /*
       */

      m_qxmlStreamReader.skipCurrentElement();
    }

  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not an observed_in element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::read_psimod(pappso::Peptide &peptide)
{

  qDebug() << m_qxmlStreamReader.name();
  QString at  = m_qxmlStreamReader.attributes().value("at").toString();
  QString acc = m_qxmlStreamReader.attributes().value("acc").toString();
  if(at.isEmpty())
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("the psimod tag must have an at attribute."));
    }
  if(acc.isEmpty())
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("the psimod tag must have an acc attribute."));
    }
  try
    {
      pappso::AaModificationP modification =
        pappso::AaModification::getInstance(acc);
      peptide.addAaModification(modification, at.toUInt() - 1);
    }
  catch(const pappso::PappsoException &e)
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("ERROR setting psimod:\n%1").arg(e.qwhat()));
    }

  m_qxmlStreamReader.skipCurrentElement();
}


void
mcql::MassChroqMl2JsonParser::readQuantificationMethods()
{

  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      read_quantification_method();
    }
}


/// <quantification_method id="my_qzivy">
void
mcql::MassChroqMl2JsonParser::read_quantification_method()
{

  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "quantification_method")
    {
      QString quantification_id =
        m_qxmlStreamReader.attributes().value("id").toString();
      if(quantification_id.isEmpty())
        {
          m_qxmlStreamReader.raiseError(QObject::tr(
            "the quantification_method tag must have an id attribute."));
        }


      // xic_extraction
      m_qxmlStreamReader.readNextStartElement();
      if(m_qxmlStreamReader.name().toString() != "xic_extraction")
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("xic_extraction element expected here"));
        }
      else
        {
          QString xic_type =
            m_qxmlStreamReader.attributes().value("xic_type").toString();
          if(xic_type == "sum")
            {
            }
          else if(xic_type == "max")
            {
            }
          else
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("the xic_extraction tag must have a xic_type "
                            "attribute whose value is 'sum' or 'max'."));
            }

          QJsonObject precision;
          if(m_qxmlStreamReader.readNextStartElement())
            {
              // mz_range
              if(m_qxmlStreamReader.name().toString() == "mz_range")
                {
                  precision.insert("unit", "dalton");
                  /// <xic_extraction>
                  /// <mz_range min="0.5" max="1.5"/>
                  QString min =
                    m_qxmlStreamReader.attributes().value("min").toString();
                  QString max =
                    m_qxmlStreamReader.attributes().value("max").toString();
                  precision.insert("down", min.toDouble());
                  precision.insert("up", max.toDouble());
                  if(min.isEmpty())
                    {
                      m_qxmlStreamReader.raiseError(QObject::tr(
                        "the mz_range tag must have a min attribute."));
                    }
                  if(max.isEmpty())
                    {
                      m_qxmlStreamReader.raiseError(QObject::tr(
                        "the mz_range tag must have a max attribute."));
                    }

                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "ppm_range")
                {
                  /// <xic_extraction>
                  /// <ppm_range min="" max=""/>
                  precision.insert("unit", "ppm");

                  QString min =
                    m_qxmlStreamReader.attributes().value("min").toString();
                  QString max =
                    m_qxmlStreamReader.attributes().value("max").toString();

                  precision.insert("down", min.toDouble());
                  precision.insert("up", max.toDouble());
                  if(min.isEmpty())
                    {
                      m_qxmlStreamReader.raiseError(QObject::tr(
                        "the ppm_range tag must have a min attribute."));
                    }
                  if(max.isEmpty())
                    {
                      m_qxmlStreamReader.raiseError(QObject::tr(
                        "the ppm_range tag must have a max attribute."));
                    }
                }
              else
                {
                  m_qxmlStreamReader.raiseError(
                    QObject::tr("missing mz_range or ppm_range element"));
                }
            }

          else
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("missing mz_range or ppm_range element"));
            }

          m_qxmlStreamReader.skipCurrentElement();
          if(m_qxmlStreamReader.readNextStartElement())
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("%1 element not allowed here")
                  .arg(m_qxmlStreamReader.name()));
            }
          QJsonObject extraction;
          extraction.insert("integration", xic_type);
          extraction.insert("precision", precision);

          m_jsonQuantificationMethod.insert("extraction", extraction);
          m_jsonQuantificationMethod.insert("match_between_run", true);
          m_jsonQuantificationMethod.insert("isotope_minimum_ratio", 0.8);
          qDebug() << m_qxmlStreamReader.name();
        }
      qDebug() << m_qxmlStreamReader.name();


      m_qxmlStreamReader.readNextStartElement();
      // xic_filters
      if(m_qxmlStreamReader.name().toString() == "xic_filters")
        {
          read_xic_filters();
          // peak_detection
          m_qxmlStreamReader.readNextStartElement();
        }

      qDebug() << m_qxmlStreamReader.name();
      read_peak_detection();

      qDebug() << m_qxmlStreamReader.name();
      if(m_qxmlStreamReader.readNextStartElement())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("%1 element not allowed here")
              .arg(m_qxmlStreamReader.name()));
        }
      qDebug() << m_qxmlStreamReader.name();
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("Not a quantification_method element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::read_peak_detection()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "peak_detection")
    {
      if(m_qxmlStreamReader.readNextStartElement())
        {

          if(m_qxmlStreamReader.name().toString() == "detection_zivy")
            {
              read_detection_zivy();
            }
          else if(m_qxmlStreamReader.name().toString() == "detection_moulon")
            {
              read_detection_moulon();
            }

          else
            {
              m_qxmlStreamReader.raiseError(QObject::tr(
                "detection_zivy or detection_moulon element expected"));
              m_qxmlStreamReader.skipCurrentElement();
            }
        }
      else
        {

          m_qxmlStreamReader.raiseError(
            QObject::tr("missing detection_zivy or detection_moulon element"));
        }


      if(m_qxmlStreamReader.readNextStartElement())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("%1 element not allowed here")
              .arg(m_qxmlStreamReader.name()));
        }

      qDebug() << m_qxmlStreamReader.name();
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("Not a peak_detection element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::read_xic_filters()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "xic_filters")
    {
      pappso::FilterSuiteString filter_suite("");
      while(m_qxmlStreamReader.readNextStartElement())
        {
          read_xic_filter(filter_suite);
        }
      m_jsonQuantificationMethod.insert("pre_filter", filter_suite.toString());
    }

  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not a xic_filters element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}

void
mcql::MassChroqMl2JsonParser::read_xic_filter(
  pappso::FilterSuiteString &filter_suite)
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "anti_spike")
    {
      /// <anti_spike half="8">
      QString half = m_qxmlStreamReader.attributes().value("half").toString();
      if(half.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the anti_spike tag must have a half attribute."));
        }

      pappso::FilterMorphoAntiSpike f_spike(half.toDouble());
      filter_suite.addFilterFromString(f_spike.toString());
    }
  else if(m_qxmlStreamReader.name().toString() == "background")
    {

      //<background half_mediane="5" half_min_max="20">
      QString half_mediane =
        m_qxmlStreamReader.attributes().value("half_mediane").toString();
      QString half_min_max =
        m_qxmlStreamReader.attributes().value("half_min_max").toString();

      if(half_mediane.isEmpty())
        {
          m_qxmlStreamReader.raiseError(QObject::tr(
            "the background tag must have a half_mediane attribute."));
        }
      if(half_min_max.isEmpty())
        {
          m_qxmlStreamReader.raiseError(QObject::tr(
            "the background tag must have a half_min_max attribute."));
        }

      pappso::FilterMorphoBackground f_bkg(half_mediane.toDouble(),
                                           half_min_max.toDouble());

      filter_suite.addFilterFromString(f_bkg.toString());
      /*      quantification_method.get()->addXicFilter(
        std::make_shared<pappso::FilterMorphoBackground>(f_bkg));
        */
    }

  else if(m_qxmlStreamReader.name().toString() == "remove_intensity")
    {
      QString value = m_qxmlStreamReader.attributes().value("value").toString();

      if(value.isEmpty())
        {
          m_qxmlStreamReader.raiseError(QObject::tr(
            "the remove_intensity tag must have a value attribute."));
        }

      pappso::FilterRemoveY f_remove(value.toDouble());

      filter_suite.addFilterFromString(f_remove.toString());
      /*            quantification_method.get()->addXicFilter(
std::make_shared<pappso::FilterRemoveY>(f_remove));
*/
    }

  else if(m_qxmlStreamReader.name().toString() == "remove_quantile")
    {
      QString value = m_qxmlStreamReader.attributes().value("value").toString();

      if(value.isEmpty())
        {
          m_qxmlStreamReader.raiseError(QObject::tr(
            "the remove_quantile tag must have a value attribute."));
        }

      pappso::FilterQuantileBasedRemoveY f_remove(value.toDouble());
      filter_suite.addFilterFromString(f_remove.toString());
      /*
      quantification_method.get()->addXicFilter(
        std::make_shared<pappso::FilterQuantileBasedRemoveY>(f_remove));
        */
    }

  else if(m_qxmlStreamReader.name().toString() == "smoothing")
    {
      QString half = m_qxmlStreamReader.attributes().value("half").toString();
      if(half.isEmpty())
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("the smoothing tag must have a half attribute."));
        }

      pappso::FilterMorphoMean f_smooth(half.toDouble());


      filter_suite.addFilterFromString(f_smooth.toString());
      /*
            quantification_method.get()->addXicFilter(
              std::make_shared<pappso::FilterMorphoMean>(f_smooth));
              */
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("filter element, one of smoothing, remove_quantile, "
                    "remove_intensity, background, anti_spike expected"));
      m_qxmlStreamReader.skipCurrentElement();
    }
  m_qxmlStreamReader.skipCurrentElement();
}

/// <detection_zivy>
void
mcql::MassChroqMl2JsonParser::read_detection_zivy()
{
  qDebug() << m_qxmlStreamReader.name();

  QJsonObject detection;
  if(m_qxmlStreamReader.name().toString() == "detection_zivy")
    {
      detection.insert("type", "zivy");
      while(m_qxmlStreamReader.readNextStartElement())
        {
          qDebug() << m_qxmlStreamReader.name();
          if(m_qxmlStreamReader.name().toString() == "mean_filter_half_edge")
            {
              /// <detection_zivy>
              /// <mean_filter_half_edge>3</mean_filter_half_edge>
              /*
              sp_detection_zivy.get()->setFilterMorphoMean(
                pappso::FilterMorphoMean(
                  m_qxmlStreamReader.readElementText().toDouble()));
                  */
              detection.insert("meanfilter",
                               m_qxmlStreamReader.readElementText().toInt());
            }
          else if(m_qxmlStreamReader.name().toString() == "minmax_half_edge")
            {
              /// <detection_zivy>
              /// <minmax_half_edge>3</minmax_half_edge>
              /*
              sp_detection_zivy.get()->setFilterMorphoMinMax(
                pappso::FilterMorphoMinMax(
                  m_qxmlStreamReader.readElementText().toDouble()));
                  */
              detection.insert("minmax",
                               m_qxmlStreamReader.readElementText().toInt());
            }
          else if(m_qxmlStreamReader.name().toString() == "maxmin_half_edge")
            {

              /// <detection_zivy>
              /// <maxmin_half_edge>3</maxmin_half_edge>
              /*
              sp_detection_zivy.get()->setFilterMorphoMaxMin(
                pappso::FilterMorphoMaxMin(
                  m_qxmlStreamReader.readElementText().toDouble()));
                  */
              detection.insert("maxmin",
                               m_qxmlStreamReader.readElementText().toInt());
            }

          else if(m_qxmlStreamReader.name().toString() ==
                  "detection_threshold_on_max")
            {

              /// <detection_zivy>
              /// <detection_threshold_on_max>5000</detection_threshold_on_max>
              /*
              sp_detection_zivy.get()->setDetectionThresholdOnMinmax(
                m_qxmlStreamReader.readElementText().toDouble());
                */
              detection.insert("threshold_on_max",
                               m_qxmlStreamReader.readElementText().toDouble());
            }

          else if(m_qxmlStreamReader.name().toString() ==
                  "detection_threshold_on_min")
            {
              /// <detection_zivy>
              /// <detection_threshold_on_min>3000</detection_threshold_on_min>
              /*
              sp_detection_zivy.get()->setDetectionThresholdOnMaxmin(
                m_qxmlStreamReader.readElementText().toDouble());
                */
              detection.insert("threshold_on_min",
                               m_qxmlStreamReader.readElementText().toDouble());
            }

          else
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("detection_zivy element has no %1 element")
                  .arg(m_qxmlStreamReader.name()));
            }

          // m_qxmlStreamReader.skipCurrentElement();
        }
      m_jsonQuantificationMethod.insert("detection", detection);

      qDebug() << m_qxmlStreamReader.name();
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("Not a detection_zivy element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}


void
mcql::MassChroqMl2JsonParser::read_detection_moulon()
{
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "detection_moulon")
    {
      /// <detection_moulon>

      while(m_qxmlStreamReader.readNextStartElement())
        {
          qDebug() << m_qxmlStreamReader.name();
          if(m_qxmlStreamReader.name().toString() == "TIC_stop")
            {
              /// <detection_moulon>
              /// <TIC_stop>4</TIC_stop>
              /*
              sp_current_detection_moulon->setTicStop(
                m_qxmlStreamReader.readElementText().toDouble());
                */
            }
          else if(m_qxmlStreamReader.name().toString() == "TIC_start")
            {
              /// <detection_moulon>
              /// <TIC_start>4</TIC_start>
              /*
              sp_current_detection_moulon->setTicStart(
                m_qxmlStreamReader.readElementText().toDouble());
                */
            }
          else if(m_qxmlStreamReader.name().toString() == "smoothing_point")
            {
              /// <detection_moulon>
              /// <smoothing_point>5</smoothing_point>
              /*
              sp_current_detection_moulon->setFilterMorphoMean(
                m_qxmlStreamReader.readElementText().toDouble());
                */
            }
          else
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("detection_moulon element has no %1 element")
                  .arg(m_qxmlStreamReader.name()));
            }
        }
    }

  else
    {
      m_qxmlStreamReader.raiseError(
        QObject::tr("Not a detection_moulon element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonProjectParameters() const
{
  return m_jsonProjectParameters;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonProteinList() const
{
  return m_jsonProteinList;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonMsRunList() const
{
  return m_jsonMsRunList;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonPeptideList() const
{
  return m_jsonPeptideList;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonMsRunPeptideList() const
{
  return m_jsonMsRunPeptideList;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonAlignmentMethod() const
{
  return m_jsonAlignmentMethod;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonQuantificationMethod() const
{
  return m_jsonQuantificationMethod;
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonQuantifyGroup() const
{
  return m_jsonQuantifyGroup;
}

void
mcql::MassChroqMl2JsonParser::populateJsonDocument(
  QJsonDocument &document) const
{

  QJsonObject root;
  QJsonObject identification_data;


  identification_data.insert("msrun_list", getJsonMsRunList());
  identification_data.insert("peptide_list", getJsonPeptideList());
  identification_data.insert("msrunpeptide_list", getJsonMsRunPeptideList());
  identification_data.insert("protein_list", getJsonProteinList());
  root.insert("project_parameters", getJsonProjectParameters());
  root.insert("identification_data", identification_data);

  QJsonObject methods;
  methods.insert("alignment_method", getJsonAlignmentMethod());

  methods.insert("quantification_method", getJsonQuantificationMethod());

  root.insert("masschroq_methods", methods);

  QJsonObject action;
  /*
   *
"action": {
    "match_between_run": true,
    "isotope_minimum_ratio": 0.9,
    "group_list": { "g1": ["msruna1","msruna2","msruna3"]
    },
    "quantify_group": {"g1": {
        "alignment_reference":  "msruna1"
    }}
}*/
  action.insert("group_list", getJsonGroupList());
  action.insert("align_group", getJsonQuantifyGroup());
  action.insert("quantify_all", "true");

  root.insert("actions", action);
  document.setObject(root);
}

const QJsonObject &
mcql::MassChroqMl2JsonParser::getJsonGroupList() const
{
  return m_jsonGroupList;
}

void
mcql::MassChroqMl2JsonParser::readGroups()
{
  qDebug();
  qDebug() << m_qxmlStreamReader.name();
  if(m_qxmlStreamReader.name().toString() == "groups")
    {

      while(m_qxmlStreamReader.readNextStartElement())
        {
          qDebug() << m_qxmlStreamReader.name();
          if(m_qxmlStreamReader.name().toString() == "group")
            {
              //<group data_ids="samp0 samp1" id="G1" />
              QJsonArray group;
              QStringList id_list = m_qxmlStreamReader.attributes()
                                      .value("data_ids")
                                      .toString()
                                      .split(" ");
              for(QString id : id_list)
                {
                  group.append(id);
                }

              m_jsonGroupList.insert(
                m_qxmlStreamReader.attributes().value("id").toString(), group);
              m_qxmlStreamReader.skipCurrentElement();
            }
          else
            {
              m_qxmlStreamReader.raiseError(
                QObject::tr("Not a group element %1")
                  .arg(m_qxmlStreamReader.name()));
              m_qxmlStreamReader.skipCurrentElement();
            }
        }
    }
  else
    {
      m_qxmlStreamReader.raiseError(QObject::tr("Not a groups element"));
      m_qxmlStreamReader.skipCurrentElement();
    }
}
