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

/*******************************************************************************
 * 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/>.
 *
 * Contributors:
 *     Olivier Langella <olivier.langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

#include "tandemcondorprocess.h"
#include <QDebug>
#include <QSettings>
#include <QProcess>
#include <QThread>
#include <pappsomspp/core/pappsoexception.h>
#include "../../files/tandemparametersfile.h"
#include "../../utils/utils.h"

TandemCondorProcess::TandemCondorProcess(MainWindow *p_main_window,
                                         WorkMonitorInterface *p_monitor,
                                         const TandemRunBatch &tandem_run_batch)
  : TandemBatchProcess(p_monitor, tandem_run_batch),
    CondorProcess(p_main_window, p_monitor, "X!Tandem")
{
  /*
  Universe   = vanilla
  notification   = Error
  Rank       = Mips
  request_memory= 50000
  request_cpus = 1
  Executable = /usr/bin/tandem
  Log        =
  /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/submit_condor.log
  Output        =
  /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/tandem.$(Process).out
  Error        =
  /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/tandem.$(Process).error

  Arguments =
  /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/xtandem_param2054956555872858570.xml
  Queue
  */

  QSettings settings;
  m_condorRequestMemory =
    settings.value("tandem/condor_request_memory", "10000").toUInt();
  m_condorDiskUsage =
    settings.value("tandem/condor_disk_usage", "1000").toUInt();
}

TandemCondorProcess::~TandemCondorProcess()
{
}

void
TandemCondorProcess::prepareXmlDatabaseFile()
{

  QFile xml_database_file(QString("%1/database.xml").arg(mp_tmpDir->path()));

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

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

  prepareTemporaryDirectory();

  TandemParametersFile orig_parameter_file(_tandem_run_batch._preset_file);
  _preset_file = QString("%1/%2")
                   .arg(mp_tmpDir->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));
  new_parameter_file.setTandemParameters(new_param);

  prepareXmlDatabaseFile();


  // condor submit file :
  QFile submit_file(QString("%1/submit.txt").arg(mp_tmpDir->path()));
  QTextStream *p_out = nullptr;

  if(submit_file.open(QIODevice::WriteOnly))
    {
      p_out = new QTextStream();
      p_out->setDevice(&submit_file);

      *p_out << "Universe   = vanilla" << Qt::endl;
      *p_out << "notification   = Error" << Qt::endl;
      *p_out << "Rank       = Mips" << Qt::endl;
      *p_out << "DiskUsage  = " << m_condorDiskUsage << Qt::endl;
      *p_out << "request_memory= " << m_condorRequestMemory << Qt::endl;
      *p_out << "request_cpus = " << _tandem_run_batch._number_of_threads
             << Qt::endl;
      *p_out << "Log        = " << mp_tmpDir->path() << "/condor.log"
             << Qt::endl;
      *p_out << "Output        = " << mp_tmpDir->path()
             << "/tandem.$(Process).out" << Qt::endl;
      *p_out << "Error        = " << mp_tmpDir->path()
             << "/tandem.$(Process).error" << Qt::endl;
      /*
      Log        =
      /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/submit_condor.log
      Output        =
      /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/tandem.$(Process).out
      Error        =
      /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/tandem.$(Process).error

      Arguments =
      /gorgone/pappso/tmp/temp_condor_job93294001891239208719639434471283743/xtandem_param2054956555872858570.xml
      Queue
      */
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("error : cannot open condor submit file : %1\n")
          .arg(submit_file.fileName()));
    }


  std::vector<QTemporaryFile *> input_file_list;

  int i = 0;
  _p_monitor->setTotalSteps(_tandem_run_batch._mz_file_list.size());
  for(QString mz_file : _tandem_run_batch._mz_file_list)
    {


      QTemporaryFile *p_xml_input_file =
        new QTemporaryFile(QString("%1/tandem").arg(mp_tmpDir->path()));

      input_file_list.push_back(p_xml_input_file);
      p_xml_input_file->setAutoRemove(false);
      if(p_xml_input_file->open())
        {
          QXmlStreamWriter *p_xml_out = new QXmlStreamWriter();
          p_xml_out->setDevice(p_xml_input_file);

          writeXmlInputFile(p_xml_out, mz_file);

          p_xml_input_file->close();
          delete p_xml_out;
          if(Utils::guessDataFileFormatFromFile(mz_file) !=
             pappso::Enums::MsDataFormat::unknown)
            {
              QSettings settings;
              QString tims_tmp_dir_path =
                settings.value("timstof/tmp_dir_path", QDir::tempPath())
                  .toString();
              if(!QDir(tims_tmp_dir_path).exists())
                {
                  QDir().mkdir(tims_tmp_dir_path);
                }

              *p_out << "Executable = " << _tandem_run_batch._tandem_bin_path
                     << Qt::endl;
              *p_out
                << "Arguments        = "
                << QFileInfo(p_xml_input_file->fileName()).absoluteFilePath()
                << Qt::endl;
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("The loaded file:\n %1 \n hasn't a known format")
                  .arg(mz_file));
            }
          *p_out << "Queue" << Qt::endl;
        }
      else
        {
          throw pappso::PappsoException(
            QObject::tr(
              "error : cannot open the XML X!Tandem input file : %1\n")
              .arg(p_xml_input_file->fileName()));
        }
      i++;
    }
  for(QTemporaryFile *p_xml_input_file : input_file_list)
    {
      delete p_xml_input_file;
    }


  if(p_out != nullptr)
    {
      p_out->flush();
      submit_file.close();
      delete p_out;
    }


  // now run condor job on submit_file


  QStringList arguments;

  arguments << QFileInfo(submit_file.fileName()).absoluteFilePath();
  try
    {
      QProcess condor_process;
      // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
      qDebug() << "TandemCondorProcess::run command " << m_condorSubmitCommand
               << " " << arguments.join(" ");
      condor_process.start(m_condorSubmitCommand, arguments);


      if(!condor_process.waitForStarted())
        {
          QString err = QObject::tr(
                          "Could not start condor_submit process "
                          "'%1' with arguments '%2': %3")
                          .arg(m_condorSubmitCommand)
                          .arg(arguments.join(QStringLiteral(" ")))
                          .arg(condor_process.errorString());
          throw pappso::PappsoException(err);
        }

      if(!condor_process.waitForFinished(_max_xt_time_ms))
        {
          throw pappso::PappsoException(
            QObject::tr("HTCondor X!Tandem process failed to finish"));
        }

      QString perr = condor_process.readAllStandardError();
      if(perr.length())
        {

          qDebug() << "TandemCondorProcess::run readAllStandardError " << perr;
          throw pappso::PappsoException(
            QObject::tr("HTCondor X!Tandem process failed :\n%1").arg(perr));
        }
      else
        {
          qDebug() << "TandemCondorProcess::run readAllStandardError OK "
                   << perr;
        }

      QString pjob = condor_process.readAllStandardOutput();
      if(pjob.length())
        {
          qDebug() << "TandemCondorProcess::run readAllStandardOutput OK "
                   << pjob;
        }
      else
        {
          qDebug() << "TandemCondorProcess::run readAllStandardOutput " << pjob;
          throw pappso::PappsoException(
            QObject::tr("HTCondor X!Tandem process failed :\n%1").arg(pjob));
        }

      // Submitting job(s).\n1 job(s) submitted to cluster 29.\n
      parseCondorJobNumber(pjob);

      _p_monitor->setTotalSteps(m_condorJobSize);
      qDebug() << "TandemCondorProcess::run job=" << m_condorClusterNumber
               << " size=" << m_condorJobSize;


      /*
      if (!xt_process->waitForFinished(_max_xt_time_ms)) {
          throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem
      process to finish : timeout at %1").arg(_max_xt_time_ms));
      }
      */
      QByteArray result = condor_process.readAll();

      QProcess::ExitStatus Status = condor_process.exitStatus();

      qDebug() << "TandemCondorProcess::run ExitStatus " << Status
               << result.data();
      if(Status != 0)
        {
          // != QProcess::NormalExit
          throw pappso::PappsoException(
            QObject::tr("error executing HTCondor Status != 0 : %1 %2\n%3")
              .arg(_tandem_run_batch._tandem_bin_path)
              .arg(arguments.join(" ").arg(result.data())));
        }
    }
  catch(pappso::PappsoException &exception_pappso)
    {
      throw pappso::PappsoException(
        QObject::tr("error launching X!Tandem condor jobs :\n%1")
          .arg(exception_pappso.qwhat()));
    }
  catch(std::exception &exception_std)
    {
      throw pappso::PappsoException(
        QObject::tr(
          "error launching X!Tandem condor jobs (std::exception):\n%1")
          .arg(exception_std.what()));
    }
  surveyCondorJob();

  _p_monitor->finished(QObject::tr("%1 HTCondor X!Tandem job(s) finished")
                         .arg(_tandem_run_batch._mz_file_list.size()));
  qDebug() << "TandemCondorProcess::run end";
}
