/**
 * \file pappsomspp/processing/tandemwrapper/xtandemoutputsaxhandler.cpp
 * \date 01/02/2020
 * \author Olivier Langella
 * \brief rewrites tandem xml input file with temporary files
 */

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


#include "xtandemoutputsaxhandler.h"
#include "../../../pappsomspp/pappsoexception.h"
#include <QFileInfo>

namespace pappso
{
XtandemOutputSaxHandler::XtandemOutputSaxHandler(
  const QString &final_tandem_output, const QString &original_msdata_file_name)
  : m_destinationTandemOutputFile(final_tandem_output)
{
  qDebug() << final_tandem_output;
  m_originalMsDataFileName =
    QFileInfo(original_msdata_file_name).absoluteFilePath();
  if(!m_destinationTandemOutputFile.open(QIODevice::WriteOnly))
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: unable to open %1 to write XML output")
          .arg(final_tandem_output));
    }
  p_writeXmlTandemOutput = new QXmlStreamWriter();
  p_writeXmlTandemOutput->setAutoFormatting(true);
  p_writeXmlTandemOutput->setDevice(&m_destinationTandemOutputFile);
}

XtandemOutputSaxHandler::~XtandemOutputSaxHandler()
{
  if(p_writeXmlTandemOutput != nullptr)
    {
      m_destinationTandemOutputFile.close();
      delete p_writeXmlTandemOutput;
    }
}

void
XtandemOutputSaxHandler::writeOpenTag(const QString &qName,
                                      const QXmlAttributes &attributes)
{
  p_writeXmlTandemOutput->writeStartElement(qName);
  for(int i = 0; i < attributes.length(); i++)
    {
      p_writeXmlTandemOutput->writeAttribute(attributes.qName(i),
                                             attributes.value(i));
    }
}

bool
XtandemOutputSaxHandler::startElement(
  [[maybe_unused]] const QString &namespaceURI,
  [[maybe_unused]] const QString &localName,
  const QString &qName,
  const QXmlAttributes &attributes)
{
  m_tagStack.push_back(qName);
  bool is_ok = true;

  try
    {
      m_currentText.clear();
      //<bioml label="example api document">
      if(m_tagStack.size() == 1)
        {
          if(qName != "bioml")
            {
              m_errorString =
                QObject::tr(
                  "ERROR in XtandemOutputSaxHandler::startElement "
                  "root tag %1 is not <bioml>")
                  .arg(qName);
              m_isTandemParameter = false;
              return false;
            }
          else
            {

              m_isTandemParameter = true;

              // <bioml xmlns:GAML="http://www.bioml.com/gaml/" label="models
              // from '/tmp/msdata.mzxml'">
              p_writeXmlTandemOutput->writeStartElement("bioml");
              p_writeXmlTandemOutput->writeAttribute(
                "xmlns:GAML", attributes.value("xmlns:GAML"));
              p_writeXmlTandemOutput->writeAttribute(
                "label",
                QString("models from '%1'").arg(m_originalMsDataFileName));
              return true;
            }
        }
      // startElement_group

      if(qName == "note")
        {
          is_ok = startElement_note(attributes);
        }
      else
        {
          writeOpenTag(qName, attributes);
        }
    }
  catch(const pappso::PappsoException &exception_pappso)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemOutputSaxHandler::startElement "
                        "tag %1, PAPPSO exception:\n%2")
                        .arg(qName)
                        .arg(exception_pappso.qwhat());
      return false;
    }
  catch(const std::exception &exception_std)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemOutputSaxHandler::startElement "
                        "tag %1, std exception:\n%2")
                        .arg(qName)
                        .arg(exception_std.what());
      return false;
    }
  return is_ok;
}

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

  bool is_ok = true;
  // endElement_peptide_list
  try
    {

      if(qName == "note")
        {
          is_ok = endElement_note();
        }
      else
        {
          p_writeXmlTandemOutput->writeEndElement();
        }
    }
  catch(const pappso::PappsoException &exception_pappso)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemOutputSaxHandler::endElement tag "
                        "%1, PAPPSO exception:\n%2")
                        .arg(qName)
                        .arg(exception_pappso.qwhat());
      return false;
    }
  catch(const std::exception &exception_std)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemOutputSaxHandler::endElement tag "
                        "%1, std exception:\n%2")
                        .arg(qName)
                        .arg(exception_std.what());
      return false;
    }

  m_currentText.clear();
  m_tagStack.pop_back();

  return is_ok;
}

bool
XtandemOutputSaxHandler::startDocument()
{

  p_writeXmlTandemOutput->setAutoFormatting(true);
  p_writeXmlTandemOutput->writeStartDocument("1.0");
  return true;
}

bool
XtandemOutputSaxHandler::endDocument()
{
  p_writeXmlTandemOutput->writeEndDocument();

  m_destinationTandemOutputFile.close();
  delete p_writeXmlTandemOutput;
  p_writeXmlTandemOutput = nullptr;
  return true;
}

bool
XtandemOutputSaxHandler::characters(const QString &str)
{
  m_currentText += str;
  if(m_tagStack.back() != "note")
    {
      p_writeXmlTandemOutput->writeCharacters(str);
    }
  return true;
}


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


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

QString
XtandemOutputSaxHandler::errorString() const
{
  return m_errorString;
}


bool
XtandemOutputSaxHandler::startElement_note(QXmlAttributes attributes)
{
  // qDebug() << "XtandemParamSaxHandler::startElement_note begin " <<
  // <note type="input"
  // label="output,path">/gorgone/pappso/jouy/users/Celine/2019_Lumos/20191222_107_Juste_APD/metapappso_condor/test_run/20191222_18_EF1_third_step_test_condor_22janv.xml</note>

  writeOpenTag("note", attributes);
  m_currentLabel = "";

  if(attributes.value("type") == "input")
    {
      m_currentLabel = attributes.value("label");
    }

  //  qDebug() << "XtandemParamSaxHandler::startElement_note _current_label " <<
  //  _current_label;
  return true;
}

bool
XtandemOutputSaxHandler::endElement_note()
{
  //    qDebug() << "XtandemParamSaxHandler::endElement_note begin " <<
  //   <note type="input" label="spectrum,
  //   path">/tmp/tandemwrapper-IehrEL/msdata.mzxml</note>

  if(m_currentLabel == "spectrum, path")
    {
      //<note type="input"
      // label="spectrum,path">/gorgone/pappso/jouy/raw/2019_Lumos/20191222_107_Juste/20191222_18_EF1.mzXML</note>
      // m_originMzDataFileName = m_currentText;
      // p_writeXmlTandemOutput->writeCharacters(m_destinationMzXmlFileName);
      p_writeXmlTandemOutput->writeCharacters(m_originalMsDataFileName);
      p_writeXmlTandemOutput->writeEndElement();


      for(auto pair_input : m_mapTandemInputParameters)
        {
          p_writeXmlTandemOutput->writeStartElement("note");
          p_writeXmlTandemOutput->writeAttribute("type", "input");
          p_writeXmlTandemOutput->writeAttribute("label", pair_input.first);
          p_writeXmlTandemOutput->writeCharacters(pair_input.second);
          p_writeXmlTandemOutput->writeEndElement();
        }
    }
  else if(m_currentLabel == "output, path")
    {
      //<note type="input" label="output,
      // path">/tmp/tandemwrapper-sSGxtE/output_tandem.xml</note>
      p_writeXmlTandemOutput->writeCharacters(
        QFileInfo(m_destinationTandemOutputFile).absoluteFilePath());
      p_writeXmlTandemOutput->writeEndElement();
    }
  else
    {
      p_writeXmlTandemOutput->writeCharacters(m_currentText);
      p_writeXmlTandemOutput->writeEndElement();
    }
  return true;
}

void
XtandemOutputSaxHandler::setInputParameters(const QString &label_name_attribute,
                                            const QString &input_value)
{
  m_mapTandemInputParameters.insert(
    std::pair<QString, QString>(label_name_attribute, input_value));
}
} // namespace pappso
