/**
 * \file /core/masschroq_run/masschroqbatchprocess.cpp
 * \date 2/11/2020
 * \author Thomas Renne
 * \brief handles execution of a bunch of MassChroQ process
 */

/*******************************************************************************
 * Copyright (c) 2020 Thomas Renne <thomas.renne@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 "masschroqbatchprocess.h"
#include "../../utils/utils.h"
#include <pappsomspp/core/exception/exceptioninterrupted.h>
#include <pappsomspp/core/masschroq/input/jsoninput.h>
#include "../../cli/export/cborstreamreadertsv.h"

MassChroQBatchProcess::MassChroQBatchProcess(
  MainWindow *p_main_window,
  WorkMonitorInterface *p_monitor,
  const MassChroQRunBatch masschroq_batch_param)
{
  mp_mainWindow       = p_main_window;
  mp_monitor          = p_monitor;
  m_masschroqRunBatch = masschroq_batch_param;
}

MassChroQBatchProcess::~MassChroQBatchProcess()
{
}

void
MassChroQBatchProcess::run()
{
  m_mcqErrorString = "";
  mp_monitor->setTotalSteps(1);

  // Check if the bin exist and is executable then check the masschroqml file
  // mp_monitor->message(QObject::tr("running MassChroQ checks \n"), 0);
  // checkMassChroQRunBatch();
  // checkMassChroQMLValidity();
  qDebug() << "checks finished";

  // Set the maschroq bin and the arguments
  mp_monitor->message(QObject::tr("running MassChroQ on %1\n")
                        .arg(m_masschroqRunBatch.masschroq_json_path),
                      1);
  QStringList arguments;
  QString masschroq_path;
  qDebug();
  masschroq_path = m_masschroqRunBatch.masschroq_bin_path;

  arguments << "-c" << QString::number(m_masschroqRunBatch.number_cpu);
  if(m_masschroqRunBatch.isTrace)
    {
      arguments << "--trace";
    }
  // qWarning() << "shape:" << m_masschroqRunBatch.isShape << " " <<
  // m_masschroqRunBatch.shapeMargin;
  if(m_masschroqRunBatch.isShape)
    {
      arguments << "--shape"
                << QString("%1").arg(m_masschroqRunBatch.shapeMargin);
    }
  if(!m_masschroqRunBatch.masschroq_temporary_dir_path.isEmpty())
    {
      arguments << "-t" << m_masschroqRunBatch.masschroq_temporary_dir_path;
    }
  arguments << "--cbor" << m_masschroqRunBatch.getCborFilePath();

  arguments << m_masschroqRunBatch.masschroq_json_path;


  qDebug();
  // Run MassChroQ with the given arguments and the masschroqml
  mpa_mcqProcess = new QProcess();


  qDebug();
  connect(mpa_mcqProcess,
          &QProcess::readyReadStandardOutput,
          this,
          &MassChroQBatchProcess::readyReadStandardOutput);
  connect(mpa_mcqProcess,
          &QProcess::readyReadStandardError,
          this,
          &MassChroQBatchProcess::readyReadStandardError);

  qDebug();
  mpa_mcqProcess->start(masschroq_path, arguments);

  qDebug();
  if(!mpa_mcqProcess->waitForStarted())
    {
      QString err = QObject::tr(
                      "Could not start MassChroQ process "
                      "'%1' with arguments '%2': %3")
                      .arg(masschroq_path)
                      .arg(arguments.join(QStringLiteral(" ")))
                      .arg(mpa_mcqProcess->errorString());
      throw pappso::PappsoException(err);
    }

  qDebug();
  while(mpa_mcqProcess->waitForFinished(1000) == false)
    {
      // mp_monitor->appendText(mpa_mcqProcess->readAll().data());
      qDebug();
      if(mp_monitor->shouldIstop())
        {
          qDebug() << "killing";
          mpa_mcqProcess->kill();
          delete mpa_mcqProcess;
          throw pappso::ExceptionInterrupted(
            QObject::tr("MassChroQ stopped by the user"));
        }
    }
  qDebug();
  // Stop the proccess and check if the exit was good
  QProcess::ExitStatus Status = mpa_mcqProcess->exitStatus();
  qDebug() << "QProcess::ExitStatus=" << Status;
  delete mpa_mcqProcess;

  if(Status != QProcess::ExitStatus::NormalExit)
    {
      throw pappso::PappsoException(
        QObject::tr("error executing MassChroQ Status != 0 : %1 %2\n%3")
          .arg(m_masschroqRunBatch.masschroq_bin_path)
          .arg(arguments.join(" ").arg(m_mcqErrorString)));
    }
  else
    {
      // check if MassChroQ did not return an error
      if(!m_mcqErrorString.isEmpty())
        {
          throw pappso::PappsoException(
            QObject::tr("MassChroQ was stopped due to an issue :\n%1")
              .arg(m_mcqErrorString));
        }
    }


  arguments.clear();
  qDebug();
  masschroq_path = QString(m_masschroqRunBatch.masschroq_bin_path)
                     .replace("masschroq3", "masschroq-export");
  arguments << "--tsvd" << m_masschroqRunBatch.getTsvDirectoryPath();
  arguments << m_masschroqRunBatch.getCborFilePath();

  qDebug();
  // Run MassChroQ with the given arguments and the masschroqml
  mpa_mcqProcess = new QProcess();


  qDebug();
  connect(mpa_mcqProcess,
          &QProcess::readyReadStandardOutput,
          this,
          &MassChroQBatchProcess::readyReadStandardOutput);
  connect(mpa_mcqProcess,
          &QProcess::readyReadStandardError,
          this,
          &MassChroQBatchProcess::readyReadStandardError);

  qDebug();
  mpa_mcqProcess->start(masschroq_path, arguments);

  qDebug();
  if(!mpa_mcqProcess->waitForStarted())
    {
      QString err = QObject::tr(
                      "Could not start MassChroQ process "
                      "'%1' with arguments '%2': %3")
                      .arg(masschroq_path)
                      .arg(arguments.join(QStringLiteral(" ")))
                      .arg(mpa_mcqProcess->errorString());
      throw pappso::PappsoException(err);
    }

  qDebug();
  while(mpa_mcqProcess->waitForFinished(1000) == false)
    {
      // mp_monitor->appendText(mpa_mcqProcess->readAll().data());
      qDebug();
      if(mp_monitor->shouldIstop())
        {
          qDebug() << "killing";
          mpa_mcqProcess->kill();
          delete mpa_mcqProcess;
          throw pappso::ExceptionInterrupted(
            QObject::tr("MassChroQ stopped by the user"));
        }
    }
  qDebug();
  // Stop the proccess and check if the exit was good
  Status = mpa_mcqProcess->exitStatus();
  qDebug() << "QProcess::ExitStatus=" << Status;
  delete mpa_mcqProcess;

  if(Status != QProcess::ExitStatus::NormalExit)
    {
      throw pappso::PappsoException(
        QObject::tr("error executing MassChroQ Status != 0 : %1 %2\n%3")
          .arg(m_masschroqRunBatch.masschroq_bin_path)
          .arg(arguments.join(" ").arg(m_mcqErrorString)));
    }
  else
    {
      // check if MassChroQ did not return an error
      if(!m_mcqErrorString.isEmpty())
        {
          throw pappso::PappsoException(
            QObject::tr("MassChroQ was stopped due to an issue :\n%1")
              .arg(m_mcqErrorString));
        }
    }


  mp_monitor->finished(QObject::tr("%1 MassChroQ job(s) finished").arg(1));
}

void
MassChroQBatchProcess::checkMassChroQRunBatch()
{
  // Check the MassChroQ version format
  try
    {
      Utils::checkMassChroQVersion(m_masschroqRunBatch.masschroq_bin_path);
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(error);
    }

  // Check if the masschroqml exist then check if is readable
  QFileInfo masschroqml_file_info(m_masschroqRunBatch.masschroq_json_path);
  if(!masschroqml_file_info.exists())
    {
      throw pappso::PappsoException(
        QObject::tr("%1 doesn't exist!\nPlease check your masschroq json file")
          .arg(m_masschroqRunBatch.masschroq_json_path));
    }
  if(!masschroqml_file_info.isReadable())
    {
      throw pappso::PappsoException(
        QObject::tr("%1 isn't readable please check the file rights!")
          .arg(m_masschroqRunBatch.masschroq_json_path));
    }

  // Check if the given masschroq temp dir exist and is a directory (only if a
  // temp dir is given)
  if(!m_masschroqRunBatch.masschroq_temporary_dir_path.isEmpty())
    {
      QFileInfo masschroq_temp_dir(
        m_masschroqRunBatch.masschroq_temporary_dir_path);
      if(!masschroq_temp_dir.exists() || !masschroq_temp_dir.isDir())
        {
          throw pappso::PappsoException(
            QObject::tr(
              "%1 doesn't exist or is not a directory.\nPlease change "
              "the directory path or create it!")
              .arg(m_masschroqRunBatch.masschroq_temporary_dir_path));
        }
      qDebug() << masschroq_temp_dir.absoluteDir().absolutePath();
      if(!masschroq_temp_dir.absoluteDir().isReadable())
        {
          throw pappso::PappsoException(
            QObject::tr(
              "%1 is not readable!\nPlease check the directory rights")
              .arg(m_masschroqRunBatch.masschroq_temporary_dir_path));
        }
    }
}

void
MassChroQBatchProcess::checkMassChroQMLValidity()
{
  // set the masschroq bin and the arguments
  QString masschroq_path = m_masschroqRunBatch.masschroq_bin_path;
  QStringList arguments;
  QStringList results;

  arguments << "--validate" << m_masschroqRunBatch.masschroq_json_path;

  // Run MassChroQ with the validate arguments
  QProcess *mcq_process = new QProcess();
  mcq_process->start(masschroq_path, arguments);

  if(!mcq_process->waitForStarted())
    {
      QString err = QObject::tr(
                      "Could not start MassChroQ process "
                      "'%1' with arguments '%2': %3")
                      .arg(masschroq_path)
                      .arg(arguments.join(QStringLiteral(" ")))
                      .arg(mpa_mcqProcess->errorString());
      throw pappso::PappsoException(err);
    }

  while(mcq_process->waitForFinished(1000) == false)
    {
      //_p_monitor->appendText(xt_process->readAll().data());
      // data.append(xt_process->readAll());
      if(mp_monitor->shouldIstop())
        {
          mcq_process->kill();
          delete mcq_process;
          mcq_process = nullptr;
          throw pappso::ExceptionInterrupted(
            QObject::tr("MassChroQ stopped by the user"));
        }
    }


  // check if masschroq exit normally
  QProcess::ExitStatus Status = mcq_process->exitStatus();
  delete mcq_process;
  if(Status != 0)
    {
      mcq_process->kill();
      delete mcq_process;
      throw pappso::PappsoException(
        QObject::tr("error executing MassChroQ Status != 0 : %1 %2\n%3")
          .arg(m_masschroqRunBatch.masschroq_bin_path)
          .arg(arguments.join(" ").arg(results.join("\n"))));
    }
  qDebug();
}


void
MassChroQBatchProcess::readyReadStandardOutput()
{
  QString message(mpa_mcqProcess->readAllStandardOutput());
  mp_monitor->appendText(message);

  if(mp_monitor->shouldIstop())
    {
      qDebug() << "killing";
      mpa_mcqProcess->kill();
      delete mpa_mcqProcess;
      mpa_mcqProcess = nullptr;
      throw pappso::ExceptionInterrupted(
        QObject::tr("MassChroQ stopped by the user"));
    }
}

void
MassChroQBatchProcess::readyReadStandardError()
{
  QString message(mpa_mcqProcess->readAllStandardError());
  mp_monitor->appendText(message);
  m_mcqErrorString += message;
  if(mp_monitor->shouldIstop())
    {
      qDebug() << "killing";
      mpa_mcqProcess->kill();
      delete mpa_mcqProcess;
      mpa_mcqProcess = nullptr;
      throw pappso::ExceptionInterrupted(
        QObject::tr("MassChroQ stopped by the user"));
    }
}

void
MassChroQBatchProcess::runQuanti()
{

  mp_monitor->setTotalSteps(1);

  // Check if the bin exist and is executable then check the masschroqml file
  // mp_monitor->message(QObject::tr("running MassChroQ checks \n"), 0);
  // checkMassChroQRunBatch();
  // checkMassChroQMLValidity();
  qDebug() << "checks finished";

  // Set the maschroq bin and the arguments
  mp_monitor->message(QObject::tr("running MassChroQ on %1\n")
                        .arg(m_masschroqRunBatch.masschroq_json_path),
                      1);
  qDebug() << m_masschroqRunBatch.masschroq_json_path;
  QJsonParseError parseError;
  QFile json_in(m_masschroqRunBatch.masschroq_json_path);


  if(!json_in.open(QIODevice::ReadOnly))
    {
      throw pappso::PappsoException(
        QObject::tr("error reading json from file %1")
          .arg(m_masschroqRunBatch.masschroq_json_path));
    }


  QByteArray jsonData = json_in.readAll();
  // ConsoleOut::mcq_cerr() << jsonData;
  QJsonDocument json_document = QJsonDocument::fromJson(jsonData, &parseError);

  if(parseError.error != QJsonParseError::ParseError::NoError)
    {
      throw pappso::PappsoException(
        QObject::tr("error parsing json from stdin, at byte %2:\n%1")
          .arg(parseError.errorString())
          .arg(parseError.offset));
    }

  ///// JSON file OK


  // ConsoleOut::mcq_cout() << json_document.toJson();
  pappso::masschroq::JsonInput parser(
    *mp_monitor,
    m_masschroqRunBatch.masschroq_temporary_dir_path,
    json_document);

  QFile cbor_file(m_masschroqRunBatch.getCborFilePath());

  if(!cbor_file.open(QIODevice::WriteOnly))
    {
      throw pappso::PappsoException(
        QObject::tr("error opening file %1")
          .arg(m_masschroqRunBatch.getCborFilePath()));
    }

  pappso::masschroq::CborOutputStream cbor_output(&cbor_file);
  cbor_output.setIsPeakShapeOutput(m_masschroqRunBatch.isShape,
                                   m_masschroqRunBatch.shapeMargin);
  cbor_output.setIsTraceOutput(m_masschroqRunBatch.isTrace);
  parser.action(cbor_output);
  cbor_output.close();
  cbor_file.close();


  mcql::CborStreamReaderTsv tsv_out(m_masschroqRunBatch.getTsvDirectoryPath());

  qDebug() << m_masschroqRunBatch.getCborFilePath();
  QFile f(m_masschroqRunBatch.getCborFilePath());
  mp_monitor->setStatus(
    QString("reading file %1").arg(m_masschroqRunBatch.getCborFilePath()));
  if(f.open(QIODevice::ReadOnly))
    {
      qDebug();
      tsv_out.readCbor(&f, *mp_monitor);
      // if (err)
      //   return EXIT_FAILURE;
    }
  else
    {
      qDebug();
    }
}
