#include "xtandemresultshandler.h"
#include <QString>
#include <QDebug>
#include <cmath>
#include <pappsomspp/core/psm/xtandem/xtandemhyperscore.h>

XtandemResultsHandler::XtandemResultsHandler(PrecisionPtr precision)
  : _precision(precision)
{

  _ion_list.push_back(Enums::PeptideIon::y);
  _ion_list.push_back(Enums::PeptideIon::b);
}

XtandemResultsHandler::~XtandemResultsHandler()
{
}


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

  try
    {
      // startElement_group
      if(qName == "group")
        {
          is_ok = startElement_group(attributes);
        }
      else if(qName == "domain")
        {
          is_ok = startElement_domain(attributes);
        }
      else if(qName == "aa")
        {
          is_ok = startElement_aa(attributes);
        }
      else if(qName == "peptide")
        {
          is_ok = startElement_peptide(attributes);
        }
      _currentText.clear();
    }
  catch(PappsoException &exception_pappso)
    {
      _errorStr = QObject::tr(
                    "ERROR in XtandemResultsHandler::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 XtandemResultsHandler::startElement "
                    "tag %1, std exception:\n%2")
                    .arg(qName)
                    .arg(exception_std.what());
      return false;
    }
  return is_ok;
}

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

  bool is_ok = true;
  // endElement_peptide_list
  try
    {
      if(qName == "group")
        {
          is_ok = endElement_group();
        }
      else if(qName == "domain")
        {
          is_ok = endElement_domain();
        }
      // endElement_GAML__values
      else if(qName == "GAML:values")
        {
          is_ok = endElement_GAML__values();
        }

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

  _currentText.clear();
  _tag_stack.pop_back();

  return is_ok;
}

bool
XtandemResultsHandler::startElement_group(QXmlAttributes attributes)
{
  if((_tag_stack.size() > 1) && (_tag_stack[_tag_stack.size() - 2] == "bioml"))
    {


      // <group id="15046" mh="1256.720337" z="2" rt="PT2683.15S"
      // expect="2.9e-05" label="GRMZM2G083841_P01 P04711 Phosphoenolpyruvate
      // carboxylase 1 (PEPCase 1)(PEPC
      // 1)(EC..." type="model" sumI="6.37" maxI="387371" fI="3873.71" act="0" >
      _current_group_id      = attributes.value("id");
      _current_z             = attributes.value("z").toUInt();
      _current_protein_label = attributes.value("label");
      return true;
    }
  if((_tag_stack.size() > 1) && (_tag_stack[_tag_stack.size() - 2] == "group"))
    {
      //<group type="support" label="fragment ion mass spectrum">
      if(attributes.value("type") == "support")
        {
          if(attributes.value("label") == "fragment ion mass spectrum")
            {
              _in_GAML_spectrum = true;
            }
        }
    }
  return true;
}


bool
XtandemResultsHandler::startElement_peptide(QXmlAttributes attributes)
{
  if((_tag_stack.size() > 1) &&
     (_tag_stack[_tag_stack.size() - 2] == "protein"))
    {
      //<peptide start="1" end="250">
      _current_peptide_start = attributes.value("start").toUInt();
      _current_peptide_end   = attributes.value("end").toUInt();
      return true;
    }
  return true;
}

bool
XtandemResultsHandler::startElement_domain(QXmlAttributes attributes)
{
  if((_tag_stack.size() > 1) &&
     (_tag_stack[_tag_stack.size() - 2] == "peptide"))
    {
      //<domain id="2038.2.1" start="13" end="28" expect="2.8e-05"
      // mh="1622.8389" delta="0.0006" hyperscore="35.1" nextscore="14.3"
      // y_score="12.5" y_ions="9" b_score="12.2" b_ions="3" pre="PAAK"
      // post="KPKA" seq="KPAEEEPAAEKAPAGK" missed_cleavages="1">
      _current_domain_id = attributes.value("id");
      qDebug() << "startElement_domain " << _current_domain_id;
      _current_hyperscore   = attributes.value("hyperscore").toFloat();
      _current_y_ions       = attributes.value("y_ions").toUInt();
      _current_b_ions       = attributes.value("b_ions").toUInt();
      _current_seq          = attributes.value("seq");
      _current_domain_start = attributes.value("start").toUInt();
      _current_domain_end   = attributes.value("end").toUInt();
      //<peptide start="1" end="250">
      _current_mh = attributes.value("mh").toDouble();

      Peptide peptide(_current_seq);


      _current_peptide_sp = peptide.makeNoConstPeptideSp();
      return true;
    }
  return true;
}


bool
XtandemResultsHandler::startElement_aa(QXmlAttributes attributes)
{

  //<aa type="M" at="624" modified="15.99491" />
  //<aa type="K" at="8" modified="42.01056" />
  qDebug() << "startElement_aa " << _current_peptide_sp.get()->getSequence()
           << " is alive";
  // char aa                = attributes.value("type")[0].toLatin1();
  uint at                = attributes.value("at").toUInt();
  pappso_double mod_mass = attributes.value("modified").toDouble();

  uint position       = at - _current_domain_start;
  AaModificationP mod = AaModification::getInstanceXtandemMod(
    attributes.value("type"), mod_mass, _current_peptide_sp, position);
  qDebug() << mod->getAccession();
  _current_peptide_sp.get()->addAaModification(mod, position);
  qDebug() << "startElement_aa end";
  return true;
}


bool
XtandemResultsHandler::endElement_domain()
{
  if((_tag_stack.size() > 1) &&
     (_tag_stack[_tag_stack.size() - 2] == "peptide"))
    {
      qDebug() << _current_peptide_sp.get()->getSequence() << " is alive";

      if(_current_peptide_sp.get()->matchPeak(_precision, _current_mh, 1))
        {
          QString message =
            QObject::tr("we have a match domain id %1 with the current peptide")
              .arg(_current_domain_id);
          qDebug() << message;
        }
      else
        {
          _errorStr =
            QObject::tr("pappso peptide %1 mh %2 != tandem peptide mh %3")
              .arg(_current_peptide_sp.get()->getSequence())
              .arg(_current_peptide_sp.get()->getMz(1))
              .arg(_current_mh);
          qDebug() << _errorStr;
          return false;
          // throw PappsoException();
        }
    }
  else
    {
    }
  return true;
}

bool
XtandemResultsHandler::endElement_group()
{
  _in_GAML_spectrum = false;
  if((_tag_stack.size() > 1) && (_tag_stack[_tag_stack.size() - 2] == "group"))
    {
    }
  else
    {
      // XtandemHyperscore hyperscore(_curent_spectrum, _current_peptide_sp,
      // _precision, _ion_list, _max_charge,_refine_spectrum_synthesis);
    }
  return true;
}


bool
XtandemResultsHandler::endElement_GAML__values()
{

  //<GAML:Xdata label="15046.spectrum" units="MASSTOCHARGERATIO">
  //<GAML:values byteorder="INTEL" format="ASCII" numvalues="100">
  if(_in_GAML_spectrum)
    {
      // qDebug() << "XtandemResultsHandler::endElement_GAML__values a1";
      if((_tag_stack.size() > 1) &&
         (_tag_stack[_tag_stack.size() - 2] == "GAML:Xdata"))
        {
          // qDebug() << _currentText;
          _current_xdata = _currentText.split(" ");
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values a2";
        }
      else if((_tag_stack.size() > 1) &&
              (_tag_stack[_tag_stack.size() - 2] == "GAML:Ydata"))
        {
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values 1";
          _curent_spectrum.clear();
          QStringList _current_ydata = _currentText.split(" ");
          for(int i = 0; i < _current_ydata.size(); ++i)
            {
              _curent_spectrum.push_back(DataPoint(
                _current_xdata[i].toDouble(), _current_ydata[i].toDouble()));
              //_curent_spectrum.debugPrintValues();
            }
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values 2";
          XtandemHyperscore hyperscore_withxtspectrum(_curent_spectrum,
                                                      _current_peptide_sp,
                                                      _current_z,
                                                      _precision,
                                                      _ion_list,
                                                      true);
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values 3";
          float test_tandem =
            round(10 * hyperscore_withxtspectrum.getHyperscore()) / 10;
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values 4";
          if(test_tandem != (float)_current_hyperscore)
            {
              _errorStr =
                QObject::tr(
                  "protein: %1 \ntest_tandem %2 != %3 (yions %4 vs "
                  "%5) (bions %6 vs %7)")
                  .arg(_current_protein_label)
                  .arg(test_tandem)
                  .arg(_current_hyperscore)
                  .arg(hyperscore_withxtspectrum.getMatchedIons(Enums::PeptideIon::y))
                  .arg(_current_y_ions)
                  .arg(hyperscore_withxtspectrum.getMatchedIons(Enums::PeptideIon::b))
                  .arg(_current_b_ions);
              qDebug() << _errorStr;
              return false;
            }
          else
            {
              _errorStr =
                QObject::tr(
                  "protein: %1 \ntest_tandem %2 == %3 (yions %4 vs "
                  "%5) (bions %6 vs %7)")
                  .arg(_current_protein_label)
                  .arg(test_tandem)
                  .arg(_current_hyperscore)
                  .arg(hyperscore_withxtspectrum.getMatchedIons(Enums::PeptideIon::y))
                  .arg(_current_y_ions)
                  .arg(hyperscore_withxtspectrum.getMatchedIons(Enums::PeptideIon::b))
                  .arg(_current_b_ions);
              qDebug() << _errorStr;
            }
          // qDebug() << "XtandemResultsHandler::endElement_GAML__values 5";
        }
    }
  return true;
}

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

  return false;
}


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

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


bool
XtandemResultsHandler::endDocument()
{
  return true;
}

bool
XtandemResultsHandler::startDocument()
{
  return true;
}

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