/**
 * \file pappsomspp/processing/tandemwrapper/xtandeminputsaxhandler.cpp
 * \date 25/01/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 "xtandeminputsaxhandler.h"
#include "../../pappsoexception.h"
#include <QFileInfo>
namespace pappso
{
XtandemInputSaxHandler::XtandemInputSaxHandler(
  const QString &destinationMzXmlFile,
  const QString &destinationTandemInputFile,
  const QString &destinationTandemOutputFile)
  : m_destinationTandemInputFile(destinationTandemInputFile)
{
  m_destinationMzXmlFileName        = destinationMzXmlFile;
  m_destinationTandemOutputFileName = destinationTandemOutputFile;
  m_destinationTandemInputFileName =
    QFileInfo(destinationTandemInputFile).absoluteFilePath();

  if(destinationTandemInputFile.isEmpty())
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: X!Tandem input file path is empty"));
    }

  if(!m_destinationTandemInputFile.open(QIODevice::WriteOnly))
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: unable to open %1 tandem output file for write")
          .arg(destinationTandemInputFile));
    }

  p_writeXmlTandemInput = new QXmlStreamWriter();
  p_writeXmlTandemInput->setDevice(&m_destinationTandemInputFile);
}

XtandemInputSaxHandler::~XtandemInputSaxHandler()
{
  if(p_writeXmlTandemInput != nullptr)
    {
      m_destinationTandemInputFile.close();
      delete p_writeXmlTandemInput;
    }
}

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

bool
XtandemInputSaxHandler::startElement(
  [[maybe_unused]] const QString &namespaceURI,
  [[maybe_unused]] const QString &localName,
  const QString &qName,
  const QXmlAttributes &attributes)
{
  /*
<?xml version="1.0" encoding="UTF-8"?>
<bioml label="20191222_18_EF1_test_condor_22janv_third_step.xml">
<note type="heading">Paths</note>
<note type="input" label="list path, default
parameters">/gorgone/pappso/jouy/presets/metapappso/Lumos_04112019_PROTEOCARDIS_THIRD_STEP_AB.xml</note>
<note type="input" label="list path, taxonomy
information">/gorgone/pappso/jouy/users/Celine/2019_Lumos/20191222_107_Juste_APD/metapappso_condor/params/xtandem_database_third_step_test_condor_22janv.database</note>
<note type="input" label="spectrum,
path">/gorgone/pappso/jouy/raw/2019_Lumos/20191222_107_Juste/20191222_18_EF1.mzXML</note>
<note type="heading">Protein general</note>
<note type="input" label="protein, taxon">usedefined</note>
<note type="heading">Output</note>
<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>
</bioml>
*/
  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 XtandemInputSaxHandler::startElement "
                                "root tag %1 is not <bioml>")
                                .arg(qName);
              m_isTandemParameter = false;
              return false;
            }
          else
            {

              m_isTandemParameter = true;
              // label="20191222_18_EF1_test_condor_22janv_third_step.xml"
              m_labelName = attributes.value("label");

              writeOpenTag(qName, attributes);
            }
        }
      // startElement_group

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

bool
XtandemInputSaxHandler::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_writeXmlTandemInput->writeCharacters(m_currentText);
          p_writeXmlTandemInput->writeEndElement();
        }
    }
  catch(pappso::PappsoException &exception_pappso)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemInputSaxHandler::endElement tag "
                        "%1, PAPPSO exception:\n%2")
                        .arg(qName)
                        .arg(exception_pappso.qwhat());
      return false;
    }
  catch(std::exception &exception_std)
    {
      m_errorString = QObject::tr(
                        "ERROR in XtandemInputSaxHandler::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
XtandemInputSaxHandler::startDocument()
{

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

bool
XtandemInputSaxHandler::endDocument()
{
  p_writeXmlTandemInput->writeEndDocument();

  m_destinationTandemInputFile.close();
  delete p_writeXmlTandemInput;
  p_writeXmlTandemInput = nullptr;
  return true;
}

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


bool
XtandemInputSaxHandler::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
XtandemInputSaxHandler::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
XtandemInputSaxHandler::errorString() const
{
  return m_errorString;
}


bool
XtandemInputSaxHandler::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
XtandemInputSaxHandler::endElement_note()
{
  //    qDebug() << "XtandemParamSaxHandler::endElement_note begin " <<
  //    _current_label << " " << _current_text.simplified();
  if(m_currentLabel == "output, path")
    {
      m_originTandemOutpuFileName = m_currentText;
      p_writeXmlTandemInput->writeCharacters(m_destinationTandemOutputFileName);
    }
  else if(m_currentLabel == "list path, default parameters")
    {
      // <note type="input" label="list path, default parameters">
      p_writeXmlTandemInput->writeCharacters(m_currentText);
      m_originTandemPresetFileName =
        QFileInfo(m_currentText).absoluteFilePath();
    }
  else 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_writeXmlTandemInput->writeCharacters(m_destinationMzXmlFileName);
    }
  else
    {
      p_writeXmlTandemInput->writeCharacters(m_currentText);
    }
  p_writeXmlTandemInput->writeEndElement();
  return true;
}


const QString &
XtandemInputSaxHandler::getOriginalMsDataFileName() const
{
  return m_originMzDataFileName;
}

const QString &
XtandemInputSaxHandler::getOriginalTandemOutputFileName() const
{
  return m_originTandemOutpuFileName;
}

const QString &
XtandemInputSaxHandler::getOriginalTandemPresetFileName() const
{
  return m_originTandemPresetFileName;
}
} // namespace pappso
