/**
 * \file /input/masschroq/masscgroqmlinfoparser.cpp
 * \date 27/05/2023
 * \author Olivier Langella
 * \brief extracts important informations from masschroqML file
 */

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

#include <QDebug>
#include <QDir>
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include "masschroqmlinfoparser.h"

MasschroqMlInfoParser::MasschroqMlInfoParser()
{
}

MasschroqMlInfoParser::~MasschroqMlInfoParser()
{
}

const QString &
MasschroqMlInfoParser::getTsvResultDirectoryPath() const
{
  return m_tsvdir;
}

const McqrMetadataSet &
MasschroqMlInfoParser::getMcqrMetadataSet() const
{
  return m_mcqrMetadataSet;
}


void
MasschroqMlInfoParser::readStream()
{

  m_isOk = false;
  qDebug();
  if(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();
      if(m_qxmlStreamReader.name().toString() == "masschroq")
        {
          qDebug();
          while(m_qxmlStreamReader.readNextStartElement())
            {
              qDebug() << m_qxmlStreamReader.name();

              if(m_qxmlStreamReader.name().toString() == "project_parameters")
                {
                  read_project_parameters();
                }
              else if(m_qxmlStreamReader.name().toString() == "rawdata")
                {
                  read_rawdata();
                }
              else if(m_qxmlStreamReader.name().toString() == "groups")
                {
                  read_groups();
                }
              else if(m_qxmlStreamReader.name().toString() == "protein_list")
                {
                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "peptide_list")
                {
                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "alignments")
                {
                  read_alignments();
                }
              else if(m_qxmlStreamReader.name().toString() ==
                      "quantification_methods")
                {
                  read_quantification_methods();
                }

              else if(m_qxmlStreamReader.name().toString() == "quantification")
                {
                  read_quantification();
                }
              else
                { // group
                  /*
                m_qxmlStreamReader.raiseError(
                  QObject::tr("unknown element %1")
                    .arg(m_qxmlStreamReader.name()));
                    */
                  m_qxmlStreamReader.skipCurrentElement();
                }
            }
        }
      else
        {
          m_qxmlStreamReader.raiseError(
            QObject::tr("Not a masschroqML file (no masschroq tag)"));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }

  getMcqVersion();
  if(errorString().isEmpty())
    {
      m_isOk = true;
    }
  qDebug();
}


bool
MasschroqMlInfoParser::readFile(const QString &fileName)
{
  QDir::setCurrent(QFileInfo(fileName).absolutePath());
  return pappso::XmlStreamReaderInterface::readFile(fileName);
}

void
MasschroqMlInfoParser::read_groups()
{
  /*
  <groups>
      <group id="all_msrun" data_ids="msruna1 msruna2 msruna3 msruna4 msruna5
  msruna6 msruna7 msruna8 msruna9 msrunb10 msrunb11 msrunb12 msrunb13 msrunb14
  msrunb15 msrunb16 msrunb17 msrunb18 msrunb19"/>
  </groups>
  */

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

      if(m_qxmlStreamReader.name().toString() == "group")
        {
          QString group_id =
            m_qxmlStreamReader.attributes().value("id").toString();
          QStringList data_id_list = m_qxmlStreamReader.attributes()
                                       .value("data_ids")
                                       .toString()
                                       .split(" ");

          m_mapGroups.insert({group_id, data_id_list});
          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        { // group
          m_qxmlStreamReader.raiseError(
            QObject::tr("unknown element %1").arg(m_qxmlStreamReader.name()));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}

void
MasschroqMlInfoParser::read_rawdata()
{

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

      if(m_qxmlStreamReader.name().toString() == "data_file")
        {
          //<data_file id="msruna1" format="mzxml"
          // path="/gorgone/pappso/data_extraction_pappso/mzXML/20120906_balliau_extract_1_A01_urnb-1.mzXML"
          // type="centroid"/>
          McqrMetadata metadata;
          metadata.m_msrunId =
            m_qxmlStreamReader.attributes().value("id").toString();

          metadata.m_msrunFile =
            QFileInfo(m_qxmlStreamReader.attributes().value("path").toString())
              .fileName();
          m_mcqrMetadataSet.push_back(metadata);
          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        { // group
          m_qxmlStreamReader.raiseError(
            QObject::tr("unknown element %1").arg(m_qxmlStreamReader.name()));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}

void
MasschroqMlInfoParser::read_project_parameters()
{
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();

      if(m_qxmlStreamReader.name().toString() == "project_param")
        {
          pappso::ProjectParam param;
          param.category =
            (pappso::ProjectParamCategory)m_qxmlStreamReader.attributes()
              .value("category")
              .toUInt();
          param.name = m_qxmlStreamReader.attributes().value("name").toString();
          param.value =
            m_qxmlStreamReader.attributes().value("value").toString();
          m_projectParameters.setProjectParam(param);
          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        { // group
          m_qxmlStreamReader.raiseError(
            QObject::tr("unknown element %1").arg(m_qxmlStreamReader.name()));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}


void
MasschroqMlInfoParser::read_quantification()
{


  // <quantification>
  /*
<quantification_results>
  <quantification_result output_file = "result_eme" format = "tsv" />
  <quantification_result output_file = "rdata_eme" format = "rdata" />
  <!--<quantification_result output_file = "result2" format = "ods" /> -->
  </ quantification_results><quantification_traces>
  <!--<peptide_traces peptide_ids = "pep0 pep1" output_dir =
         "pep_traces" format      = "tsv" /> --></ quantification_traces>
  <quantify id = "q1" withingroup = "all_msrun" quantification_method_id =
     "quant1">
  <peptides_in_peptide_list mode = "post_matching" ni_min_abundance = "0.8" />
  <!--<mz_list> 732.317 449.754 552.234 464.251 381.577 569.771 575.256 <
   / mz_list> -- > <!--<mzrt_list><mzrt mz = "732.317" rt = "230.712" />
                     <mzrt mz = "575.256" rt = "254.788" /></ mzrt_list> -->
  </ quantify>
*/

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

      if(m_qxmlStreamReader.name().toString() == "quantification_results")
        {

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

              if(m_qxmlStreamReader.name().toString() ==
                 "quantification_result")
                {
                  //    <quantification_result output_file = "result_eme" format
                  //    = "tsv" />
                  if(m_qxmlStreamReader.attributes()
                       .value("format")
                       .toString() == "tsv")
                    {
                      m_tsvdir = QDir(m_qxmlStreamReader.attributes()
                                        .value("output_file")
                                        .toString())
                                   .absolutePath();
                      m_tsvdir.append(".d");
                      qDebug() << m_tsvdir;
                    }

                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "compar_result")
                {
                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "peak_shape")
                {
                  m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "xic_coords")
                {
                  m_qxmlStreamReader.skipCurrentElement();
                }

              else

                { // group
                  m_qxmlStreamReader.raiseError(
                    QObject::tr("unknown element %1")
                      .arg(m_qxmlStreamReader.name()));
                  m_qxmlStreamReader.skipCurrentElement();
                }
            }
        }

      else if(m_qxmlStreamReader.name().toString() == "quantify")
        {
          /*
          <quantify id="q1" withingroup="all_msrun"
quantification_method_id="quant1"> <peptides_in_peptide_list
mode="post_matching" ni_min_abundance="0.8"/>
  <!--<mz_list>732.317 449.754 552.234 464.251 381.577 569.771
575.256</mz_list>-->
  <!--<mzrt_list>
<mzrt mz="732.317" rt="230.712" />
<mzrt mz="575.256" rt="254.788" />
</mzrt_list>-->
</quantify>*/

          QString qid = m_qxmlStreamReader.attributes().value("id").toString();
          QString withingroup =
            m_qxmlStreamReader.attributes().value("withingroup").toString();
          qDebug() << qid;
          qDebug() << withingroup;

          m_mapQuantifyIdToGroup.insert({qid, withingroup});

          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        { // group
          /*
        m_qxmlStreamReader.raiseError(
          QObject::tr("unknown element
        %1").arg(m_qxmlStreamReader.name()));
          */
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
  //  </quantification>
}


QString
MasschroqMlInfoParser::getPeptideTsvResultFilename(const QString &q_id) const
{
  // all_msrun_proteins.tsv           peptides_q1_all_msrun.tsv

  auto it_qid = m_mapQuantifyIdToGroup.find(q_id);

  if(it_qid != m_mapQuantifyIdToGroup.end())
    {

      QDir result_dir(m_tsvdir);
      QFileInfo peptide_file(result_dir.absoluteFilePath(
        QString("peptides_%1_%2.tsv").arg(it_qid->first).arg(it_qid->second)));

      if(peptide_file.exists())
        {
          return peptide_file.absoluteFilePath();
        }
      else
        {
          throw pappso::ExceptionNotFound(
            QObject::tr("peptide TSV file %1 not found")
              .arg(peptide_file.absoluteFilePath()));
        }
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("quantification id %1 not found").arg(q_id));
    }
}

void
MasschroqMlInfoParser::getMcqVersion()
{
  pappso::ProjectParam param_version(
    {pappso::ProjectParamCategory::quantification, "mcq_version", QVariant()});

  QDir result_dir(m_tsvdir);
  for(QFileInfo file_info : result_dir.entryInfoList())
    {
      if(file_info.baseName().contains("information"))
        {
          QFile f(file_info.absoluteFilePath());
          if(!f.open(QFile::ReadOnly | QFile::Text))
            break;
          QTextStream in(&f);
          // qDebug() << f.size() << in.readAll();
          //  MassChroQ version	2.4.25
          QString line;
          while(in.readLineInto(&line))
            {
              if(line.startsWith("MassChroQ version"))
                {
                  param_version.value.setValue(line.split("\t").at(1));
                  m_projectParameters.setProjectParam(param_version);
                }
            }

          f.close();
        }
    }
}


QString
MasschroqMlInfoParser::getProteinTsvResultFilename(const QString &q_id) const
{

  QDir result_dir(m_tsvdir);


  auto it_qid = m_mapQuantifyIdToGroup.find(q_id);

  if(it_qid != m_mapQuantifyIdToGroup.end())
    {


      QFileInfo protein_file(result_dir.absoluteFilePath(
        QString("%1_proteins.tsv").arg(it_qid->second)));

      if(protein_file.exists())
        {
          return protein_file.absoluteFilePath();
        }
      else
        {
          throw pappso::ExceptionNotFound(
            QObject::tr("protein TSV file %1 not found")
              .arg(protein_file.absoluteFilePath()));
        }
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("quantification id %1 not found").arg(q_id));
    }
}

const QString &
MasschroqMlInfoParser::getGroupIdbyQid(const QString &q_id) const
{

  auto it_qid = m_mapQuantifyIdToGroup.find(q_id);

  if(it_qid != m_mapQuantifyIdToGroup.end())
    {
      return it_qid->second;
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("quantification id %1 not found").arg(q_id));
    }
}


const QString &
MasschroqMlInfoParser::getSingleQid() const
{
  if(m_mapQuantifyIdToGroup.size() == 0)
    {
      throw pappso::ExceptionNotFound(QObject::tr("no quantification id"));
    }
  if(m_mapQuantifyIdToGroup.size() == 1)
    {
      return m_mapQuantifyIdToGroup.begin()->first;
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("more than one quantification id"));
    }
}

McqrMetadataSet
MasschroqMlInfoParser::getMcqrMetadataSet(const QString &group_id) const
{
  McqrMetadataSet metadata_set;

  auto it_group = m_mapGroups.find(group_id);

  if(it_group != m_mapGroups.end())
    {
      for(auto data_id : it_group->second)
        {
          metadata_set.push_back(
            m_mcqrMetadataSet.getMcqrMetadaByDataId(data_id));
        }
    }
  else
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("group id %1 not found").arg(group_id));
    }
  return metadata_set;
}

bool
MasschroqMlInfoParser::isOk() const
{
  return m_isOk;
}

void
MasschroqMlInfoParser::read_alignments()
{
  /*
  <alignment_methods>
            <alignment_method id="my_ms2">
                <ms2>
                    <!--write_time_values_output_dir="directory" to write
retention time corrections-->
                    <ms2_tendency_halfwindow>10</ms2_tendency_halfwindow>
                    <ms2_smoothing_halfwindow>15</ms2_smoothing_halfwindow>
                    <ms1_smoothing_halfwindow>0</ms1_smoothing_halfwindow>
                </ms2>
            </alignment_method>
            <!--<alignment_method id="my_obiwarp">
<obiwarp>
          <lmat_precision>1</lmat_precision>
          <mz_start>500</mz_start>
          <mz_stop>1200</mz_stop>
        </obiwarp>
      </alignment_method>-->
        </alignment_methods>
        <align group_id="All_samples" method_id="my_ms2"
reference_data_id="msruna2"/>
        */


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

      if(m_qxmlStreamReader.name().toString() == "alignment_methods")
        {
          while(m_qxmlStreamReader.readNextStartElement())
            {

              if(m_qxmlStreamReader.name().toString() == "alignment_method")
                {
                  while(m_qxmlStreamReader.readNextStartElement())
                    {
                      qDebug() << m_qxmlStreamReader.name();

                      if(m_qxmlStreamReader.name().toString() == "ms2")
                        {
                          while(m_qxmlStreamReader.readNextStartElement())
                            {
                              qDebug() << m_qxmlStreamReader.name();

                              pappso::ProjectParam project_param(
                                {pappso::ProjectParamCategory::quantification,
                                 "",
                                 QVariant()});
                              project_param.name =
                                QString("mcq_alignment_")
                                  .append(m_qxmlStreamReader.name().toString());
                              QString content;
                              while(m_qxmlStreamReader.readNext() ==
                                    QXmlStreamReader::TokenType::Characters)
                                {
                                  qDebug() << m_qxmlStreamReader.text();
                                  content += m_qxmlStreamReader.text();
                                }
                              project_param.value.setValue(content.toInt());
                              m_projectParameters.setProjectParam(
                                project_param);
                            }
                        }
                      else
                        { // group
                          m_qxmlStreamReader.raiseError(
                            QObject::tr("unknown element %1")
                              .arg(m_qxmlStreamReader.name()));
                          m_qxmlStreamReader.skipCurrentElement();
                        }
                    }
                }
            }
        }
      else if(m_qxmlStreamReader.name().toString() == "align")
        {
          //<align group_id="All_samples" method_id="my_ms2"
          // reference_data_id="msruna2"/>

          pappso::ProjectParam project_param(
            {pappso::ProjectParamCategory::quantification, "", QVariant()});

          project_param.name = "mcq_alignment_msrun_reference_for_rt_alignment";
          QString msrunid    = m_qxmlStreamReader.attributes()
                              .value("reference_data_id")
                              .toString();
          project_param.value.setValue(
            m_mcqrMetadataSet.getMcqrMetadaByDataId(msrunid).m_msrunFile);
          m_projectParameters.setProjectParam(project_param);
          m_qxmlStreamReader.skipCurrentElement();
        }
      else
        { // group
          m_qxmlStreamReader.raiseError(
            QObject::tr("unknown element %1").arg(m_qxmlStreamReader.name()));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}

pappso::ProjectParameters
MasschroqMlInfoParser::getProjectParameters() const
{
  return m_projectParameters;
}

void
MasschroqMlInfoParser::read_quantification_methods()
{
  /*
  <quantification_methods>
        <quantification_method id="quant1">
            <xic_extraction xic_type="max">
                <!--max : XIC on BasePeak; sum : XIC on TIC-->
                <!--For XIC extraction on Da use: mz_range-->
                <ppm_range min="10" max="10"/>
            </xic_extraction>
            <xic_filters>
                <anti_spike half="5"/>
                <!--<background half_mediane="5" half_min_max="20"/>-->
            </xic_filters>
            <peak_detection>
                <detection_zivy>
                    <mean_filter_half_edge>1</mean_filter_half_edge>
                    <minmax_half_edge>4</minmax_half_edge>
                    <maxmin_half_edge>3</maxmin_half_edge>
                    <detection_threshold_on_max>5000</detection_threshold_on_max>
                    <detection_threshold_on_min>3000</detection_threshold_on_min>
                </detection_zivy>
            </peak_detection>
        </quantification_method>
    </quantification_methods>
    */
  pappso::ProjectParam project_param(
    {pappso::ProjectParamCategory::quantification, "", QVariant()});
  while(m_qxmlStreamReader.readNextStartElement())
    {
      qDebug() << m_qxmlStreamReader.name();

      if(m_qxmlStreamReader.name().toString() == "quantification_method")
        {

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

              if(m_qxmlStreamReader.name().toString() == "xic_extraction")
                {
                  // xic_type="max"
                  project_param.name = "mcq_xic_extraction_type";
                  project_param.value.setValue(m_qxmlStreamReader.attributes()
                                                 .value("xic_type")
                                                 .toString());
                  m_projectParameters.setProjectParam(project_param);

                  while(m_qxmlStreamReader.readNextStartElement())
                    {

                      qDebug() << m_qxmlStreamReader.name();
                      if(m_qxmlStreamReader.name().toString() == "ppm_range")
                        {

                          //<!--For XIC extraction on Da use: mz_range-->
                          //<ppm_range min="10" max="10"/>

                          project_param.name = "mcq_xic_ppm_range_min";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("min")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);

                          project_param.name = "mcq_xic_ppm_range_max";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("max")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);
                        }
                      if(m_qxmlStreamReader.name().toString() == "mz_range")
                        {

                          //<!--For XIC extraction on Da use: mz_range-->
                          //<ppm_range min="10" max="10"/>

                          project_param.name = "mcq_xic_mz_range_min";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("min")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);

                          project_param.name = "mcq_xic_mz_range_max";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("max")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);
                        }
                      m_qxmlStreamReader.skipCurrentElement();
                    }
                  // m_qxmlStreamReader.skipCurrentElement();
                }
              else if(m_qxmlStreamReader.name().toString() == "xic_filters")
                {
                  /*<xic_filters>
                <anti_spike half="5"/>
                <!--<background half_mediane="5" half_min_max="20"/>-->
            </xic_filters>*/

                  while(m_qxmlStreamReader.readNextStartElement())
                    {
                      qDebug() << m_qxmlStreamReader.name();
                      if(m_qxmlStreamReader.name().toString() == "anti_spike")
                        {

                          project_param.name = "mcq_filter_anti_spike_half";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("half")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);
                        }

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

                          project_param.name =
                            "mcq_filter_background_half_mediane";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("half_mediane")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);


                          project_param.name =
                            "mcq_filter_background_half_min_max";
                          project_param.value.setValue(
                            m_qxmlStreamReader.attributes()
                              .value("half_min_max")
                              .toInt());
                          m_projectParameters.setProjectParam(project_param);
                        }
                      m_qxmlStreamReader.skipCurrentElement();
                    }
                  // m_qxmlStreamReader.skipCurrentElement();
                  // xic_filters
                }
              else if(m_qxmlStreamReader.name().toString() == "peak_detection")
                {
                  while(m_qxmlStreamReader.readNextStartElement())
                    {
                      qDebug() << m_qxmlStreamReader.name();
                      if(m_qxmlStreamReader.name().toString() ==
                         "detection_zivy")
                        {
                          while(m_qxmlStreamReader.readNextStartElement())
                            {

                              qDebug() << m_qxmlStreamReader.name();
                              pappso::ProjectParam project_param(
                                {pappso::ProjectParamCategory::quantification,
                                 "",
                                 QVariant()});

                              project_param.name =
                                QString("mcq_detection_")
                                  .append(m_qxmlStreamReader.name().toString());
                              QString content;
                              while(m_qxmlStreamReader.readNext() ==
                                    QXmlStreamReader::TokenType::Characters)
                                {
                                  qDebug() << m_qxmlStreamReader.text();
                                  content += m_qxmlStreamReader.text();
                                }
                              project_param.value.setValue(content.toInt());
                              m_projectParameters.setProjectParam(
                                project_param);
                              // m_qxmlStreamReader.skipCurrentElement();
                            }
                        }
                    }
                  // m_qxmlStreamReader.skipCurrentElement();
                }
              else
                { // group
                  m_qxmlStreamReader.raiseError(
                    QObject::tr("unknown element %1")
                      .arg(m_qxmlStreamReader.name()));
                  m_qxmlStreamReader.skipCurrentElement();
                }
            }
        }
      else
        { // group
          m_qxmlStreamReader.raiseError(
            QObject::tr("unknown element %1").arg(m_qxmlStreamReader.name()));
          m_qxmlStreamReader.skipCurrentElement();
        }
    }
}
