/**
 * \file /core/tandem_run/tandembatchprocess.cpp
 * \date 5/9/2017
 * \author Olivier Langella
 * \brief handles execution of a bunch of X!Tandem process
 */

/*******************************************************************************
 * Copyright (c) 2017 Olivier Langella <olivier.langella@u-psud.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 "tandembatchprocess.h"
#include <QProcess>
#include <QFileInfo>
#include <QDebug>
#include <QSettings>
#include <QTemporaryDir>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/exception/exceptioninterrupted.h>
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include <pappsomspp/core/processing/filters/filterchargedeconvolution.h>
#include "../../files/tandemparametersfile.h"
#include "../../utils/utils.h"
#include "tandemrun.h"

TandemBatchProcess::TandemBatchProcess(WorkMonitorInterface *p_monitor,
                                       const TandemRunBatch &tandem_run_batch)
{
  _tandem_run_batch = tandem_run_batch;
  _p_monitor        = p_monitor;
  _tmp_database_file.setAutoRemove(false);

  // check that output directory is writeable :
  QFileInfo fi_in_dir(_tandem_run_batch._output_directory);
  if(fi_in_dir.isDir() && fi_in_dir.isWritable())
    {
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr(
          "output directory is not writable. Please check permissions on %1")
          .arg(fi_in_dir.absoluteFilePath()));
    }
}

TandemBatchProcess::~TandemBatchProcess()
{
}

void
TandemBatchProcess::writeXmlDatabaseFile(QXmlStreamWriter *p_out)
{
  p_out->setAutoFormatting(true);
  p_out->writeStartDocument("1.0");

  //<?xml version="1.0" encoding="UTF-8"?>
  //<bioml label="x! taxon-to-file matching list">
  p_out->writeStartElement("bioml");
  p_out->writeAttribute("label", "x! taxon-to-file matching list");
  //<taxon label="usedefined">
  p_out->writeStartElement("taxon");
  p_out->writeAttribute("label", "usedefined");
  //<file format="peptide"
  // URL="/gorgone/pappso/jouy/users/Didier/fasta/20170608_Delastours/contaminants_standards.fasta"></file>
  //<file format="peptide"
  // URL="/gorgone/pappso/jouy/users/Didier/fasta/20170608_Delastours/Escherichia
  // coli 27J42_WGS_ECOLI_1.fasta"></file>
  for(QString fasta_file : _tandem_run_batch._fasta_file_list)
    {

      if(!QFileInfo(fasta_file).isWritable())
        {
          throw pappso::PappsoException(
            QObject::tr(
              "error : Fasta file %1 is not writable.\n\"tandem.exe\" requires "
              "write permission on fasta files to run correctly.")
              .arg(QFileInfo(fasta_file).absoluteFilePath()));
        }
      p_out->writeStartElement("file");
      p_out->writeAttribute("format", "peptide");
      p_out->writeAttribute("URL", fasta_file);
      p_out->writeEndElement();
    }
  //</taxon>
  p_out->writeEndElement();
  //</bioml>
  p_out->writeEndElement();
  p_out->writeEndDocument();
}

void
TandemBatchProcess::prepareXmlDatabaseFile()
{

  if(_tmp_database_file.open())
    {
      _xml_database_file =
        QFileInfo(_tmp_database_file.fileName()).absoluteFilePath();
      QXmlStreamWriter *p_out = new QXmlStreamWriter();
      p_out->setDevice(&_tmp_database_file);
      writeXmlDatabaseFile(p_out);
      _tmp_database_file.close();
      delete p_out;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("error : cannot open the XML database file : %1\n")
          .arg(_tmp_database_file.fileName()));
    }
}


void
TandemBatchProcess::run()
{
  qDebug() << _tandem_run_batch._preset_file;


  // check tandem path
  QFileInfo tandem_exe(_tandem_run_batch._tandem_bin_path);
  if(!tandem_exe.exists())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr(
          "X!Tandem software not found at %1.\nPlease check the X!Tandem "
          "installation on your computer and set tandem.exe path.")
          .arg(tandem_exe.absoluteFilePath()));
    }
  if(!tandem_exe.isReadable())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr("Please check permissions on !Tandem software found at %1.")
          .arg(tandem_exe.absoluteFilePath()));
    }


  QTemporaryDir tmp_dir;
  if(!tmp_dir.isValid())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr("problem creating temporary directory in %1\n")
          .arg(tmp_dir.path()));
    }

  TandemParametersFile orig_parameter_file(_tandem_run_batch._preset_file);
  _preset_file =
    QString("%1/%2").arg(tmp_dir.path()).arg(orig_parameter_file.getFilename());

  TandemParametersFile new_parameter_file(_preset_file);
  TandemParameters new_param = orig_parameter_file.getTandemParameters();
  //  <note type="input" label="spectrum, threads">1</note>
  new_param.setParamLabelValue(
    "spectrum, threads",
    QString("%1").arg(_tandem_run_batch._number_of_threads));

  qDebug() << "new_param.getMethodName()=" << new_param.getMethodName();
  new_parameter_file.setTandemParameters(new_param);
  qDebug() << "new_parameter_file.getMethodName()="
           << new_parameter_file.getMethodName();

  try
    {
      // m_centroid_timstof_option =
      //   new_param.getValue("spectrum, timstof MS2 centroid parameters");
    }
  catch(pappso::ExceptionNotFound &error)
    {
      // if centroid parameters are not in preset :
      // no error has to be sent, this is a normal preset
      // m_centroid_timstof_option = "";
    }

  prepareXmlDatabaseFile();
  qDebug();
  int i = 0;
  _p_monitor->setTotalSteps(_tandem_run_batch._mz_file_list.size());
  for(QString mz_file : _tandem_run_batch._mz_file_list)
    {
      _p_monitor->message(QObject::tr("running X!Tandem on %1\n").arg(mz_file),
                          i);
      runOne(mz_file);
      i++;
    }

  _p_monitor->finished(QObject::tr("%1 X!Tandem job(s) finished")
                         .arg(_tandem_run_batch._mz_file_list.size()));


  qDebug();
}

void
TandemBatchProcess::writeXmlInputFile(QXmlStreamWriter *p_out,
                                      const QString &mz_file)
{

  QFileInfo mz_file_info(mz_file);
  //<?xml version="1.0" encoding="UTF-8"?>

  p_out->setAutoFormatting(true);
  p_out->writeStartDocument("1.0");

  //<bioml label="20170405_Delastour_11.xml">
  p_out->writeStartElement("bioml");
  p_out->writeAttribute("label", mz_file_info.fileName());

  //<note type="heading">Paths</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "heading");
  p_out->writeCharacters("Paths");
  p_out->writeEndElement();

  QFileInfo test_exists(_preset_file);

  if(!test_exists.exists())
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("error : X!Tandem preset file not found %1\n")
          .arg(test_exists.absoluteFilePath()));
    }

  //<note type="input" label="list path, default
  // parameters">/gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/QExactive_IAM_4CPU_Classic_ClassicAvecReversed..xml</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "input");
  p_out->writeAttribute("label", "list path, default parameters");
  p_out->writeCharacters(_preset_file);
  p_out->writeEndElement();

  //<note type="input" label="list path, taxonomy
  // information">/gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/database.xml</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "input");
  p_out->writeAttribute("label", "list path, taxonomy information");
  p_out->writeCharacters(_xml_database_file);
  p_out->writeEndElement();


  //<note type="input" label="spectrum,
  // path">/gorgone/pappso/jouy/raw/2017_Qex/20170405_Delastours/20170405_Delastour_11.mzXML</note>

  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "input");
  p_out->writeAttribute("label", "spectrum, path");
  p_out->writeCharacters(mz_file);
  p_out->writeEndElement();

  //<note type="heading">Protein general</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "heading");
  p_out->writeCharacters("Protein general");
  p_out->writeEndElement();

  //<note type="input" label="protein, taxon">usedefined</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "input");
  p_out->writeAttribute("label", "protein, taxon");
  p_out->writeCharacters("usedefined");
  p_out->writeEndElement();
  //<note type="heading">Output</note>
  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "heading");
  p_out->writeCharacters("Output");
  p_out->writeEndElement();

  //<note type="input" label="output,
  // path">/gorgone/pappso/jouy/users/Didier/Xcal2017/2017_05_04_Delastours/Interrogation_souches_sequencees/Patient27/J42/20170405_Delastour_11.xml</note>

  p_out->writeStartElement("note");
  p_out->writeAttribute("type", "input");
  p_out->writeAttribute("label", "output, path");
  if(Utils::guessDataFileFormatFromFile(mz_file) ==
     pappso::Enums::MsDataFormat::brukerTims)
    {

      QString output_name =
        QFileInfo(mz_file_info.absoluteDir().dirName()).baseName();
      qDebug() << output_name;
      p_out->writeCharacters(QString("%1/%2.xml")
                               .arg(_tandem_run_batch._output_directory)
                               .arg(output_name));
    }
  else
    {
      p_out->writeCharacters(QString("%1/%2.xml")
                               .arg(_tandem_run_batch._output_directory)
                               .arg(mz_file_info.baseName()));
    }
  p_out->writeEndElement();

  //</bioml>
  p_out->writeEndElement();


  p_out->writeEndDocument();
}

void
TandemBatchProcess::runOne(const QString &mz_file)
{
  qDebug();
  m_currentMzFile = mz_file;
  QTemporaryFile xml_input_file;
  xml_input_file.setAutoRemove(true);
  if(xml_input_file.open())
    {
      QXmlStreamWriter *p_out = new QXmlStreamWriter();
      p_out->setDevice(&xml_input_file);

      writeXmlInputFile(p_out, mz_file);

      xml_input_file.close();
      delete p_out;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("error : cannot open the XML X!Tandem input file : %1\n")
          .arg(xml_input_file.fileName()));
    }


  QStringList arguments;
  QString tandem_path;
  QSettings settings;

  // we use the tandem wrapper for mzML and brukerTims
  QTextStream outputStream(stdout, QIODevice::WriteOnly);
  try
    {
      // QProcess::ExitStatus Status = xt_process->exitStatus();
      qDebug();
      const QDateTime dt_begin = QDateTime::currentDateTime();
      QSettings settings;
      QString tmpDirOption =
        settings.value("timstof/tmp_dir_path", QDir::tempPath()).toString();
      if(!QDir(tmpDirOption).exists())
        {
          QDir().mkdir(tmpDirOption);
        }


      TandemRun tandem_run(_tandem_run_batch._tandem_bin_path);

      //_p_monitor->setText("");
      tandem_run.run(_p_monitor->getSubMonitor(), xml_input_file.fileName());


      qDebug();
    }
  catch(pappso::ExceptionInterrupted &error)
    {

      throw pappso::ExceptionInterrupted(
        QObject::tr("X!Tandem interrupted :\n%1").arg(error.qwhat()));
    }
  catch(pappso::PappsoException &error)
    {

      throw pappso::PappsoException(
        QObject::tr("error executing TandemRun :\n%1").arg(error.qwhat()));
    }

  catch(std::exception &error)
    {
      throw pappso::PappsoException(
        QObject::tr("error executing TandemRun :\n%1").arg(error.what()));
    }
  qDebug();
}
