/**
 * \file /gui/widgets/widget_rprocess/widgetrprocess.cpp
 * \date 25/05/2023
 * \author Olivier Langella
 * \brief dialog window to drive R process
 */

/*******************************************************************************
 * Copyright (c) 2023 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.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 "widgetrprocess.h"

#include "../../mainwindow.h"
#include "ui_widget_r_process.h"
#include "../../../utils/utils.h"
#include <pappsomspp/core/exception/exceptionnotpossible.h>

WidgetRprocess::WidgetRprocess(QWidget *parent)
  : QWidget(parent), ui(new Ui::WidgetRprocess)
{
  qDebug();
  ui->setupUi(this);

  mp_documentText = new QTextDocument();
  ui->outputTextBrowser->setDocument(mp_documentText);

  // Set the Loading icon
  mp_loadingIcon = new QMovie(":icons/resources/icons/icon_wait.gif");
  ui->spinningLabel->setMovie(mp_loadingIcon);

  setWorking(false);
  mp_loadingIcon->setScaledSize(QSize(20, 20));
  mp_loadingIcon->start();

  // Create R Process and begin MCQR
  mp_rProcess = new McqrQProcess();
  mp_rProcess->startRprocess();

  connect(mp_rProcess,
          &QProcess::readyReadStandardOutput,
          this,
          &WidgetRprocess::updateRresultsBrowser);
  connect(mp_rProcess,
          &QProcess::readyReadStandardError,
          this,
          &WidgetRprocess::handleRmessages);

  connect(
    mp_rProcess, &QProcess::finished, this, &WidgetRprocess::handleFinished);
}

WidgetRprocess::~WidgetRprocess()
{
  qDebug() << "Close MCQR";
  mp_rProcess->stopRprocess();
  delete mp_loadingIcon;
  delete mp_documentText;
  delete mp_rProcess;
  qDebug() << "R process closed";
}

void
WidgetRprocess::closeEvent(QCloseEvent *event)
{
  event->accept();
}

void
WidgetRprocess::updateRresultsBrowser()
{

  QString mcqr_process_results = mp_rProcess->readAllStandardOutput();
  mcqr_process_results         = mcqr_process_results.trimmed();
  Utils::transformPlainNewLineToHtmlFormat(mcqr_process_results);

  m_documentString.append(mcqr_process_results);
  // QString content = mp_documentText->toHtml() + mcqr_process_results;
  qDebug() << "mcqr_process_results=" << mcqr_process_results;

  qDebug() << "m_documentString=" << m_documentString;
  mp_documentText->setHtml(
    QString("<html><head><link rel='stylesheet' type='text/css' "
            "href=':/style/resources/templates/mcqr_stylesheet.css'></"
            "head><body>%1</body></html>")
      .arg(m_documentString));

  QTextCursor textCursor = ui->outputTextBrowser->textCursor();
  textCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
  ui->outputTextBrowser->setTextCursor(textCursor);
}

void
WidgetRprocess::handleRmessages()
{
  QString mcqr_message = mp_rProcess->readAllStandardError();
  mcqr_message         = mcqr_message.trimmed();
  qDebug() << mcqr_message;
  QStringList mcqr_messages = mcqr_message.split(QRegularExpression(
    QString("(\n|%1|%2)").arg(QChar::LineSeparator).arg(QChar::LineFeed)));
  for(QString message : mcqr_messages)
    {
      message = message.trimmed();
      // loading MCQR version 0.6.11.1
      if(message.contains("MCQR version"))
        {
          m_mcqrVersion = message.split(" ").back();
        }
      Utils::transformPlainNewLineToHtmlFormat(message);
      QRegularExpression mcqr_info_regex("^MCQRInfo: (.*)");
      QRegularExpression mcqr_begin_regex("^MCQRBegin: (.*)");
      QRegularExpression mcqr_end_regex("^MCQREnd: (.*)");
      QRegularExpression mcqr_error_regex("^Error (in|:) (.*)");
      // Error: unexpected symbol in:
      QRegularExpression mcqr_error2_regex("^Error: (.*)");

      if(mcqr_info_regex.match(message)
           .hasMatch()) // Manually added message to MCQR execution
        {
          ui->logTextBrowser->append(message);
        }
      else if(mcqr_begin_regex.match(message)
                .hasMatch()) // MCQR message at the file begining
        {
          setWorking(true);
        }
      else if(mcqr_end_regex.match(message)
                .hasMatch()) // MCQR message at the end of an execution
        {

          setWorking(false);

          emit stepFinished(m_currentStep);
          m_currentStep = "";
        }
      else if(mcqr_error_regex.match(message).hasMatch())
        {

          setWorking(false);
          handleRerrors(mcqr_message);
        }
      else if(mcqr_error2_regex.match(message).hasMatch())
        {

          setWorking(false);
          handleRerrors(mcqr_message);
        }
      else // Other messages
        {
          /*
          null device

          1

          null device

          1

          */

          QRegularExpression mcqr_clear_spaces("\\s");

          QString clear_space = message;
          clear_space.replace(mcqr_clear_spaces, QString(""));

          qDebug() << clear_space;

          QRegularExpression mcqr_prompt_regex(
            "^prompt \\[enter\\] to continue.*");
          if(mcqr_prompt_regex.match(message).hasMatch())
            {
            }
          else if(clear_space.isEmpty())
            {
              qDebug() << message;
            }
          else if((message == "<br/>") || (message == "savePlot<br/>") ||
                  (message == "1<br/>") || (message == "null device<br/>"))
            {
              qDebug() << message;
            }
          else
            {
              qDebug() << message;
              ui->logTextBrowser->append("<p style=\"color:darkred;\">" +
                                         message.replace("<br/>", "") + "</p>");
            }
        }
    }
}

void
WidgetRprocess::executeStepOnTheRecord(const QString &step,
                                       const QString &mcqr_code)
{
  qDebug() << step;
  qDebug() << mcqr_code;
  m_currentStep = step;
  if(m_isWorking)
    {
      throw pappso::ExceptionNotPossible(tr("R is busy, please wait"));
    }
  QString beginStr = QString("message(\"MCQRBegin: %1\");\n").arg(step);
  QString endStr   = QString("\nmessage(\"MCQREnd: %1\");\n").arg(step);
  executeOnTheRecord(
    QString("%1\n%2\n%3").arg(beginStr).arg(mcqr_code).arg(endStr));
}


void
WidgetRprocess::executeOnTheRecord(const QString &mcqr_code)
{
  checkIsRunning();
  // setWorking(true);
  mp_rProcess->executeOnTheRecord(mcqr_code);
}

void
WidgetRprocess::setWorking(bool is_working)
{
  m_isWorking = is_working;
  ui->spinningLabel->setVisible(is_working);
  if(is_working)
    {
      ui->label->setText(tr("R is running, please wait..."));
    }
  else
    {
      ui->label->setText(tr(""));
    }
}


void
WidgetRprocess::handleRerrors(QString error_message)
{
  setWorking(false);
  emit stepFailed(m_currentStep);
  m_currentStep = "";
  QMessageBox message_box;
  message_box.setIcon(QMessageBox::Critical);
  message_box.setWindowTitle(tr("R error"));
  message_box.setText(
    tr("R has been stopped due to an error!!\n%1").arg(error_message));
  message_box.setStandardButtons(QMessageBox::Close);
  int btn_clicked = message_box.exec();
  switch(btn_clicked)
    {
      case QMessageBox::Close:
        // emit closeMcqrRunView();
        break;
    }
}

const QString &
WidgetRprocess::getRscriptCode() const
{
  return mp_rProcess->getRscriptCode();
}

const QString
WidgetRprocess::getRmdScriptCode(const QString &title) const
{
  QString pre_r(QString("\n\n```{r}\n"));
  QString post_r(QString("\n```\n\n"));

  QString code = QString("%1%2%3").arg(pre_r).arg(getRscriptCode()).arg(post_r);
  QRegularExpression replace_cat_h1("cat\\(\"<h1>(.*)</h1>\"\\)");
  QRegularExpression replace_cat_h2("cat\\(\"<h2>(.*)</h2>\"\\)");
  QRegularExpression replace_cat_h3("cat\\(\"<h3>(.*)</h3>\"\\)");
  QRegularExpression replace_cat_h4("cat\\(\"<h4>(.*)</h4>\"\\)");
  QRegularExpression replace_cat_p("cat\\(\"<p>(.*)</p>\"\\)");
  QRegularExpression replace_cat_message("cat\\(\"(.*)\"\\)");

  code.replace(replace_cat_h1, QString("%1# \\1%2").arg(post_r).arg(pre_r));
  code.replace(replace_cat_h2, QString("%1## \\1%2").arg(post_r).arg(pre_r));
  code.replace(replace_cat_h3, QString("%1### \\1%2").arg(post_r).arg(pre_r));
  code.replace(replace_cat_h4, QString("%1#### \\1%2").arg(post_r).arg(pre_r));
  code.replace(replace_cat_p, QString("%1\\1%2").arg(post_r).arg(pre_r));

  // XIC Analysis\n\n```{r}\n\n  \n```\n\nworking directory

  QRegularExpression replace_spaces("```{r}\\s(\\s*)\\s```");
  code.replace(replace_spaces, QString("\\1"));

  code.prepend(
    QString("---\ntitle: \"%1\"\noutput: html_document\n---\n").arg(title));


  QRegularExpression replace_setwd("setwd\\(\"(.*)\"\\);");
  code.replace(
    replace_setwd,
    QString("setwd(\"\\1\");knitr::opts_knit$set(root.dir = getwd());"));
  // setwd("/gorgone/pappso/versions_logiciels_pappso/masschroq/donnees/eme/msstats");
  // knitr::opts_knit$set(root.dir = getwd());


  QRegularExpression replace_svglite("\\ssvglite\\((.*)\\);");
  code.replace(replace_svglite, QString(""));

  QRegularExpression replace_pimg_srcsvg("\\spimg_srcsvg\\((.*)\\);");
  code.replace(replace_pimg_srcsvg, QString(""));

  QRegularExpression replace_pimg_srcpng("\\spimg_srcpng\\((.*)\\);");
  code.replace(replace_pimg_srcpng, QString(""));
  QRegularExpression replace_png("\\spng\\((.*)\\);");
  code.replace(replace_png, QString(""));

  code.replace("capture.output(dev.off(), file=stderr());", "");
  return code;
}

bool
WidgetRprocess::isWorking() const
{
  return m_isWorking;
}

void
WidgetRprocess::handleFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
  qDebug() << "exitCode=" << exitCode << " QProcess::ExitStatus=" << exitStatus;
  m_processIsFinished = true;
  m_isWorking         = false;
}

void
WidgetRprocess::checkIsRunning()
{
  try
    {
      if(m_processIsFinished)
        {
          m_processIsFinished = false;
          mp_rProcess->startRprocess();
        }
    }

  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        tr("Error starting R process: %1 ").arg(error.qwhat()));
    }
}

bool
WidgetRprocess::isFinished() const
{
  return m_processIsFinished;
}

void
WidgetRprocess::waitForStep(const QString &step_name)
{
  while(m_currentStep == step_name)
    {
      QThread::msleep(500);
    }
}

const QString &
WidgetRprocess::getMcqrVersion() const
{
  return m_mcqrVersion;
}
