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

#include "xpipsaxhandler.h"
#include <pappsomspp/exception/exceptionnotfound.h>
#include <cmath>
#include "../utils/peptidestore.h"
#include "../utils/proteinstore.h"

XpipSaxHandler::XpipSaxHandler(WorkMonitorInterface *p_monitor,
                               Project *p_project)
  : _p_project(p_project)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _p_monitor = p_monitor;
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

XpipSaxHandler::~XpipSaxHandler()
{
}


bool
XpipSaxHandler::isJavaXpip() const
{
  return _is_java_xpip;
}


bool
XpipSaxHandler::startElement(const QString &namespaceURI [[maybe_unused]],
                             const QString &localName [[maybe_unused]],
                             const QString &qName,
                             const QXmlAttributes &attributes)
{
  // qDebug() << "XpipSaxHandler::startElement begin" << namespaceURI << " "
  //        << localName << " " << qName;
  _tag_stack.push_back(qName);
  bool is_ok = true;

  try
    {

      if(_tag_stack.size() == 1)
        {
          _is_java_xpip = true;
          if(qName != "xtandem_pipeline")
            {
              _is_java_xpip = false;
              throw pappso::ExceptionNotFound(
                QObject::tr("ERROR this file is not an xpip file %1")
                  .arg(qName));
            }
        }
      else
        // startElement_group
        if(qName == "match")
        {
          is_ok = startElement_match(attributes);
        }
      else if(qName == "protein")
        {
          is_ok = startElement_protein(attributes);
        }
      else if(qName == "identification")
        {
          is_ok = startElement_identification(attributes);
        }
      //<sample value="P6_08_10"/>
      else if(qName == "sample")
        {
          is_ok = startElement_sample(attributes);
        }
      else if(qName == "peptide")
        {
          is_ok = startElement_peptide(attributes);
        }
      else if(qName == "modifs_mass")
        {
          is_ok = startElement_modifs_mass(attributes);
        }
      else if(qName == "modif")
        {
          is_ok = startElement_modif(attributes);
        }
      else if(qName == "filter_params")
        {
          is_ok = startElement_filter_params(attributes);
        }
      else if(qName == "information")
        {
          is_ok = startElement_information(attributes);
        }

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

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

  bool is_ok = true;
  // endElement_peptide_list
  try
    {
      if(qName == "protein")
        {
          is_ok = endElement_protein();
        }
      else if(qName == "identification")
        {
          is_ok = endElement_identification();
        }
      else if(qName == "peptide")
        {
          is_ok = endElement_peptide();
        }
      else if(qName == "sequence")
        {
          is_ok = endElement_sequence();
        }
      else if(qName == "match")
        {
          is_ok = endElement_match();
        }

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

  _current_text.clear();
  _tag_stack.pop_back();

  return is_ok;
}

bool
XpipSaxHandler::startElement_identification(QXmlAttributes attributes
                                            [[maybe_unused]])
{

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _map_massstr_aamod.clear();
  _current_identification_group_p = _p_project->newIdentificationGroup();
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

bool
XpipSaxHandler::startElement_filter_params(QXmlAttributes attributes)
{

  //<filter_params pep_evalue="0.01" prot_evalue="-2.0" pep_number="1"
  // filter_to_all="false" database_filter="contaminants_standarts.fasta"/>
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _automatic_filter_parameters.setFilterPeptideEvalue(
    attributes.value("pep_evalue").simplified().toDouble());
  _automatic_filter_parameters.setFilterProteinEvalue(std::pow(
    (double)10.0, attributes.value("prot_evalue").simplified().toDouble()));
  _automatic_filter_parameters.setFilterMinimumPeptidePerMatch(
    attributes.value("pep_number").simplified().toUInt());
  _automatic_filter_parameters.setFilterCrossSamplePeptideNumber(false);
  if(attributes.value("filter_to_all").simplified() == "true")
    {
      _automatic_filter_parameters.setFilterCrossSamplePeptideNumber(true);
    }
  _p_project->getFastaFileStore().getInstance(
    FastaFile(attributes.value("database_filter")));
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

bool
XpipSaxHandler::startElement_information(QXmlAttributes attributes)
{

  //<information Data_Type="indiv" match_number="223"/>
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _p_project->setProjectMode(ProjectMode::combined);
  if(attributes.value("Data_Type").simplified() == "indiv")
    {
      _p_project->setProjectMode(ProjectMode::individual);
    }
  _total_protein_match = attributes.value("match_number").toUInt();

  _p_monitor->setProgressMaximumValue(_total_protein_match);
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

bool
XpipSaxHandler::startElement_modifs_mass(QXmlAttributes attributes)
{

  /*
  <modifs_list_mass><modifs_mass modvalue="-18.01056"/>
  <modifs_mass modvalue="-17.02655"/>
  <modifs_mass modvalue="15.99491"/>
  <modifs_mass modvalue="42.01057"/>
  <modifs_mass modvalue="42.01056"/>
  <modifs_mass modvalue="57.02146"/>
  </modifs_list_mass>
  */
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  QString mass_str(attributes.value("modvalue").simplified());
  pappso::pappso_double mass = mass_str.toDouble();

  pappso::AaModificationP mod = getAaModificationP(mass);

  _map_massstr_aamod[mass_str] = mod;
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

//<sample value="P6_21_23"/>
bool
XpipSaxHandler::startElement_sample(QXmlAttributes attributes)
{

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

  MsRunSp ms_run = _p_project->getMsRunStore().getInstance(
    attributes.value("value").simplified());
  ms_run.get()->setFileName(attributes.value("value").simplified());
  //_current_identification_group_p->addMsRunSp(ms_run);
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

bool
XpipSaxHandler::startElement_match(QXmlAttributes attributes)
{

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  /*
   * <match_list><match validate="true">
   */
  _p_protein_match = new ProteinMatch();
  _p_protein_match->setChecked(false);
  if(attributes.value("validate").simplified().toLower() == "true")
    {
      _p_protein_match->setChecked(true);
    }
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}
bool
XpipSaxHandler::startElement_protein(QXmlAttributes attributes)
{

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  /*
   * <protein peptide_number="268" evalue="-432.77353"
   URL="Genome_Z_mays_5a.fasta"
   * description="GRMZM2G083841_P01 P04711 Phosphoenolpyruvate carboxylase 1
   (PEPCase 1)(PEPC 1)(EC 4.1.1.31)
   * seq=translation; coord=9:61296279..61301686:1;
   parent_transcript=GRMZM2G083841_T01; parent_gene=GRMZM2G083841">
              <protein_evalue evalue="-399.36093"
   sample="20120906_balliau_extract_1_A02_urzb-1"/> <protein_evalue
   evalue="-384.54382" sample="20120906_balliau_extract_1_A01_urnb-1"/>
              <sequence>MASTKAPGPGEKHHSIDAQLRQLVPGKVSEDDKLIEYDALLVDRFLNILQDLHGPSLREFVQECYEVSADYEGKGDTTKLGELGAKLTGLAPADAILVASSILHMLNLANLAEEVQIAHRRRNSKLKKGGFADEGSATTESDIEETLKRLVSEVGKSPEEVFEALKNQTVDLVFTAHPTQSARRSLLQKNARIRNCLTQLNAKDITDDDKQELDEALQREIQAAFRTDEIRRAQPTPQDEMRYGMSYIHETVWKGVPKFLRRVDTALKNIGINERLPYNVSLIRFSSWMGGDRDGNPRVTPEVTRDVCLLARMMAANLYIDQIEELMFELSMWRCNDELRVRAEELHSSSGSKVTKYYIEFWKQIPPNEPYRVILGHVRDKLYNTRERARHLLASGVSEISAESSFTSIEEFLEPLELCYKSLCDCGDKAIADGSLLDLLRQVFTFGLSLVKLDIRQESERHTDVIDAITTHLGIGSYREWPEDKRQEWLLSELRGKRPLLPPDLPQTDEIADVIGAFHVLAELPPDSFGPYIISMATAPSDVLAVELLQRECGVRQPLPVVPLFERLADLQSAPASVERLFSVDWYMDRIKGKQQVMVGYSDSGKDAGRLSAAWQLYRAQEEMAQVAKRYGVKLTLFHGRGGTVGRGGGPTHLAILSQPPDTINGSIRVTVQGEVIEFCFGEEHLCFQTLQRFTAATLEHGMHPPVSPKPEWRKLMDEMAVVATEEYRSVVVKEARFVEYFRSATPETEYGRMNIGSRPAKRRPGGGITTLRAIPWIFSWTQTRFHLPVWLGVGAAFKFAIDKDVRNFQVLKEMYNEWPFFRVTLDLLEMVFAKGDPGIAGLYDELLVAEELKPFGKQLRDKYVETQQLLLQIAGHKDILEGDPFLKQGLVLRNPYITTLNVFQAYTLKRIRDPNFKVTPQPPLSKEFADENKPAGLVKLNPASEYPPGLEDTLILTMKGIAAGMQNTG</sequence>
            </protein>
            */
  _current_protein.setCompleteDescription(attributes.value("description"));
  _current_fasta_file_sp = _p_project->getFastaFileStore().getInstance(
    FastaFile(attributes.value("URL")));
  _current_protein.setFastaFileP(_current_fasta_file_sp.get());
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}
bool
XpipSaxHandler::startElement_peptide(QXmlAttributes attributes)
{

  //<peptide sample="20120208_Blein_rep4_1_B03_DW21-4-26-328"
  // sample_file="/gorgone/pappso/moulon/users/Melisande/test-param-masschroq/20120208_Blein_rep4_1_B03_DW21-4-26-328.xml"
  // scan="2589" scan_in_xtandem="2589" RT="603" mhplus_obser="873.5401"
  // mhplus_theo="873.5408" deltamass="-7.0E-4" sequence="IATAIEKK" pre="NPAR"
  // post="AADA" start="331" stop="338" charge="2" evalue="9.2E-4"
  // hypercorr="35.2" validate="true">
  //<modifs></modifs></peptide>

  //<modifs><modif aa="M" modvalue="15.99491" posi="17" posi_in_prot="49"/>
  //</modifs>
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _current_peptide_sp =
    PeptideXtp(attributes.value("sequence").simplified()).makePeptideXtpSp();
  MsRunSp ms_run_id = _p_project->getMsRunStore().getInstance(
    attributes.value("sample").simplified());
  _p_peptide_evidence = new PeptideEvidence(
    ms_run_id.get(), attributes.value("scan").simplified().toUInt());
  _p_peptide_evidence->setRetentionTime(
    attributes.value("RT").simplified().toDouble());
  _p_peptide_evidence->setEvalue(
    attributes.value("evalue").simplified().toDouble());
  pappso::pappso_double exp_mass =
    attributes.value("mhplus_obser").simplified().toDouble() - pappso::MHPLUS;
  _p_peptide_evidence->setExperimentalMass(exp_mass);
  _current_peptide_match.setStart(
    attributes.value("start").simplified().toUInt() - 1);
  _p_peptide_evidence->setCharge(
    attributes.value("charge").simplified().toUInt());
  _p_peptide_evidence->setParam(
    PeptideEvidenceParam::tandem_hyperscore,
    QVariant(attributes.value("hypercorr").toDouble()));

  IdentificationDataSource *p_identification_data_source =
    _p_project->getIdentificationDataSourceStore()
      .getInstance(attributes.value("sample_file").simplified())
      .get();
  _p_peptide_evidence->setIdentificationDataSource(
    p_identification_data_source);
  if(p_identification_data_source->getMsRunSp().get() == nullptr)
    {
      p_identification_data_source->setMsRunSp(ms_run_id);
    }
  if(p_identification_data_source->getMsRunSp().get() != ms_run_id.get())
    {
      throw pappso::PappsoException(QObject::tr(
        "p_identification_data_source->getMsRunSp().get() != ms_run_id.get()"));
    }
  _current_identification_group_p->addIdentificationDataSourceP(
    p_identification_data_source);
  p_identification_data_source->addFastaFile(_current_fasta_file_sp);
  _p_peptide_evidence->setChecked(false);
  if(attributes.value("validate").simplified().toLower() == "true")
    {
      _p_peptide_evidence->setChecked(true);
    }
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}

bool
XpipSaxHandler::startElement_modif(QXmlAttributes attributes)
{

  //<modifs><modif aa="M" modvalue="15.99491" posi="17" posi_in_prot="49"/>
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  pappso::AaModificationP modif =
    _map_massstr_aamod[attributes.value("modvalue").simplified()];
  unsigned int position = attributes.value("posi").simplified().toUInt();
  _current_peptide_sp.get()->addAaModification(modif, position - 1);
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return true;
}
bool
XpipSaxHandler::endElement_peptide()
{
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  PeptideXtpSp peptide_const =
    PeptideXtp(*(_current_peptide_sp.get())).makePeptideXtpSp();
  peptide_const = _p_project->getPeptideStore().getInstance(peptide_const);
  _p_peptide_evidence->setPeptideXtpSp(peptide_const);
  _current_peptide_match.setPeptideEvidenceSp(
    _p_peptide_evidence->getIdentificationDataSource()
      ->getPeptideEvidenceStore()
      .getInstance(_p_peptide_evidence));
  _p_protein_match->addPeptideMatch(_current_peptide_match);
  delete _p_peptide_evidence;
  return true;
}

bool
XpipSaxHandler::endElement_sequence()
{
  // if ((_tag_stack.size() > 1) && (_tag_stack[_tag_stack.size() - 1] ==
  // "protein")) {

  //._sequence.replace(QRegExp("\\*"), "")).removeTranslationStop()
  _current_protein.setSequence(_current_text);
  //}
  // else {
  // XtandemHyperscore hyperscore(_curent_spectrum, _current_peptide_sp,
  // _precision, _ion_list, _max_charge,_refine_spectrum_synthesis);
  //}
  return true;
}
bool
XpipSaxHandler::endElement_protein()
{

  ProteinXtpSp sp_xtp_protein = _current_protein.makeProteinXtpSp();

  _p_protein_match->setProteinXtpSp(
    _p_project->getProteinStore().getInstance(sp_xtp_protein));

  return true;
}

bool
XpipSaxHandler::endElement_identification()
{

  return true;
}

bool
XpipSaxHandler::endElement_match()
{
  _count_protein_match++;
  _p_monitor->message(QObject::tr("reading match %1").arg(_count_protein_match),
                      _count_protein_match);
  _current_identification_group_p->addProteinMatch(_p_protein_match);
  _p_protein_match = nullptr;
  return true;
}


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

  return false;
}


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

QString
XpipSaxHandler::errorString() const
{
  return _errorStr;
}


bool
XpipSaxHandler::endDocument()
{
  //_p_project->getProteinStore().setRegexpDecoyPattern(_p_project->getProteinStore().getRegexpDecoy().pattern());
  _p_project->updateAutomaticFilters(_automatic_filter_parameters);
  return true;
}

bool
XpipSaxHandler::startDocument()
{
  _count_protein_match = 0;
  return true;
}

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


pappso::AaModificationP
XpipSaxHandler::getAaModificationP(pappso::pappso_double mass) const
{
  pappso::PrecisionPtr precision =
    pappso::PrecisionFactory::getDaltonInstance(0.01);

  pappso::AaModificationP oxidation =
    pappso::AaModification::getInstance("MOD:00719");
  if(pappso::MzRange(oxidation->getMass(), precision).contains(mass))
    {
      return oxidation;
    }
  pappso::AaModificationP iodoacetamide =
    pappso::AaModification::getInstance("MOD:00397");
  if(pappso::MzRange(iodoacetamide->getMass(), precision).contains(mass))
    {
      return iodoacetamide;
    }
  pappso::AaModificationP acetylated =
    pappso::AaModification::getInstance("MOD:00408");
  if(pappso::MzRange(acetylated->getMass(), precision).contains(mass))
    {
      return acetylated;
    }
  pappso::AaModificationP phosphorylated =
    pappso::AaModification::getInstance("MOD:00696");
  if(pappso::MzRange(phosphorylated->getMass(), precision).contains(mass))
    {
      return phosphorylated;
    }
  pappso::AaModificationP ammonia =
    pappso::AaModification::getInstance("MOD:01160");
  if(pappso::MzRange(ammonia->getMass(), precision).contains(mass))
    {
      return ammonia;
    }
  pappso::AaModificationP dehydrated =
    pappso::AaModification::getInstance("MOD:00704");
  if(pappso::MzRange(dehydrated->getMass(), precision).contains(mass))
    {
      return dehydrated;
    }
  pappso::AaModificationP dimethylated =
    pappso::AaModification::getInstance("MOD:00429");
  if(pappso::MzRange(dimethylated->getMass(), precision).contains(mass))
    {
      return dimethylated;
    }

  pappso::AaModificationP dimethylated_medium =
    pappso::AaModification::getInstance("MOD:00552");
  if(pappso::MzRange(dimethylated_medium->getMass(), precision).contains(mass))
    {
      return dimethylated_medium;
    }

  pappso::AaModificationP dimethylated_heavy =
    pappso::AaModification::getInstance("MOD:00638");
  if(pappso::MzRange(dimethylated_heavy->getMass(), precision).contains(mass))
    {
      return dimethylated_heavy;
    }
  pappso::AaModificationP DimethylpyrroleAdduct =
    pappso::AaModification::getInstance("MOD:00628");
  if(pappso::MzRange(DimethylpyrroleAdduct->getMass(), precision)
       .contains(mass))
    {
      return DimethylpyrroleAdduct;
    }

  // modification not found, creating customized mod :
  return pappso::AaModification::getInstanceCustomizedMod(mass);

  throw pappso::ExceptionNotFound(
    QObject::tr("XpipSaxHandler::getAaModificationP => modification not found "
                "for mass %1")
      .arg(mass));
}
