//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#include "pqCumulusJobsPanel.h"

#include "pqNerscLoginBehavior.h"

#include "smtk/cumulus/jobspanel/cumuluswidget.h"
#include "smtk/cumulus/jobspanel/job.h"
#include "smtk/newt/qtNewtInterface.h"

#include "pqApplicationCore.h"

#include <QAction>
#include <QCheckBox>
#include <QDebug>
#include <QFormLayout>
#include <QLineEdit>
#include <QMargins>
#include <QMessageBox>
#include <QPushButton>
#include <QSslSocket>
#include <QString>
#include <QVBoxLayout>
#include <QWidget>
#include <QtGlobal>

namespace
{
const QString GIRDER_URL("https://ace3p.kitware.com/api/v1");
// const QString GIRDER_URL("http://localhost:8080/api/v1");
} // namespace

class pqCumulusJobsPanel::pqCumulusJobsPanelInternal
{
public:
  pqCumulusJobsPanelInternal();

  QWidget* MainWidget;
  QVBoxLayout* MainLayout;

  QPushButton* AuthenticateButton;
  QCheckBox* PollingCheckbox;
  QLineEdit* CumulusUrlEdit;

  cumulus::CumulusWidget* CumulusWidget;

  // Used to provide NEWT session info to other plugins
  QObject* JobsPanelObject;
};

pqCumulusJobsPanel::pqCumulusJobsPanelInternal::pqCumulusJobsPanelInternal()
  : MainWidget(nullptr)
  , MainLayout(nullptr)
  , AuthenticateButton(nullptr)
  , PollingCheckbox(nullptr)
  , CumulusUrlEdit(nullptr)
  , CumulusWidget(nullptr)
  , JobsPanelObject(nullptr)
{
}

pqCumulusJobsPanel::pqCumulusJobsPanel(QWidget* parent)
  : QDockWidget(parent)
{
  this->setObjectName("Cumulus Jobs Panel");
  this->setWindowTitle("Cumulus Jobs");
  this->Internal = new pqCumulusJobsPanelInternal;

  // Initialize main widget
  this->Internal->MainWidget = new QWidget(parent);
  this->Internal->MainLayout = new QVBoxLayout;

  newt::qtNewtInterface* newt = newt::qtNewtInterface::instance();
  bool loggedIn = !newt->userName().isEmpty();

  // Internal widget for login button and polling checkbox
  QWidget* optionsWidget = new QWidget;
  QHBoxLayout* optionsLayout = new QHBoxLayout(optionsWidget);
  QMargins margins = optionsLayout->contentsMargins();
  margins.setTop(0);
  optionsLayout->setContentsMargins(margins);

  // Insert empty widget; otherwise layout is not correct (Qt bug?)
  QWidget* placeholder = new QWidget;
  optionsLayout->addWidget(placeholder, 0, Qt::AlignLeft);

  this->Internal->AuthenticateButton = new QPushButton("Login To NERSC");
  this->Internal->AuthenticateButton->setEnabled(!loggedIn);
  optionsLayout->addWidget(this->Internal->AuthenticateButton, 0, Qt::AlignHCenter);

  this->Internal->PollingCheckbox = new QCheckBox("Polling");
  this->Internal->PollingCheckbox->setCheckState(Qt::Unchecked);
  this->Internal->PollingCheckbox->setEnabled(loggedIn);
  optionsLayout->addWidget(this->Internal->PollingCheckbox, 0, Qt::AlignRight);

  optionsWidget->setLayout(optionsLayout);
  this->Internal->MainLayout->addWidget(optionsWidget);

  QObject::connect(this->Internal->PollingCheckbox, &QCheckBox::stateChanged, [this](int state) {
    if (state == Qt::Checked)
    {
      this->Internal->CumulusWidget->startPolling();
    }
    else
    {
      this->Internal->CumulusWidget->stopPolling();
    }
  });

  // Cumulus URL
  this->Internal->CumulusUrlEdit = new QLineEdit;
  this->Internal->CumulusUrlEdit->setText(GIRDER_URL);
  this->Internal->MainLayout->addWidget(this->Internal->CumulusUrlEdit);

#ifdef NDEBUG
  // Only display cumulus URL for debug builds
  this->Internal->CumulusUrlEdit->hide();
#endif

  // Instantiate cumulus-monitoring objects
  this->Internal->CumulusWidget = new cumulus::CumulusWidget(this);
  QString cumulusUrl = this->Internal->CumulusUrlEdit->text();
  this->Internal->CumulusWidget->girderUrl(cumulusUrl);

  this->Internal->MainLayout->addWidget(this->Internal->CumulusWidget);
  connect(
    this->Internal->CumulusWidget,
    &cumulus::CumulusWidget::info,
    this,
    &pqCumulusJobsPanel::infoSlot);

  // Connect to girder when NERSC authentication completes
  QObject::connect(newt, &newt::qtNewtInterface::loginComplete, [this, newt]() {
    this->Internal->AuthenticateButton->setEnabled(false);
    this->Internal->PollingCheckbox->setEnabled(true);
    this->Internal->CumulusWidget->setEnabled(true);

    QString newtSessionId = newt->newtSessionId();
    this->Internal->CumulusWidget->authenticateGirder(newtSessionId);

    // Make newt id available to other plugins
    this->Internal->JobsPanelObject->setProperty("newt_sessionid", newtSessionId);
  });

  // Add context menu item to load results
  QAction* action = new QAction("Load Simulation Results", this);
  this->Internal->CumulusWidget->addContextMenuAction("downloaded", action);
  QObject::connect(
    action, &QAction::triggered, this, &pqCumulusJobsPanel::requestSimulationResults);

  QObject::connect(
    this->Internal->AuthenticateButton,
    &QPushButton::clicked,
    this,
    &pqCumulusJobsPanel::authenticateHPC);
  connect(
    this->Internal->CumulusWidget,
    SIGNAL(resultDownloaded(const QString&)),
    this,
    SIGNAL(resultDownloaded(const QString&)));

  // Finish main widget
  this->Internal->MainWidget->setLayout(this->Internal->MainLayout);
  this->setWidget(this->Internal->MainWidget);

  // Register JobsPanelInfo as a paraview manager, for use by other plugins
  auto pqCore = pqApplicationCore::instance();
  if (!pqCore)
  {
    qWarning() << "pqCumulusJobsPanel missing pqApplicationCore";
    return;
  }
  this->Internal->JobsPanelObject = new QObject;
  pqCore->registerManager(QString("jobs_panel"), this->Internal->JobsPanelObject);

  // This job panel is superseded by pqACE3PJobsPanel, so hide this one by default
  this->hide();
}

pqCumulusJobsPanel::~pqCumulusJobsPanel()
{
  auto pqCore = pqApplicationCore::instance();
  pqCore->unRegisterManager(QString("jobs_panel"));
  delete this->Internal;
}

void pqCumulusJobsPanel::infoSlot(const QString& msg)
{
  // Would like to emit signal to main window status bar
  // but that is not currently working. Instead, qInfo()
  // messages are sent to the CMB Log Window:
  qInfo() << "JobsPanel:" << msg;
}

void pqCumulusJobsPanel::authenticateHPC()
{
  // Check for SSL
  if (!QSslSocket::supportsSsl())
  {
    QMessageBox::critical(
      NULL,
      QObject::tr("SSL support"),
      QObject::tr("SSL support is required, you must rebuild Qt with SSL support."));
    return;
  }

  // Get cumulus/girder url
  QString cumulusUrl = this->Internal->CumulusUrlEdit->text();
  this->Internal->CumulusWidget->girderUrl(cumulusUrl);
  this->Internal->JobsPanelObject->setProperty("girder_url", cumulusUrl);

  // Check for cumulus server
  if (!this->Internal->CumulusWidget->isGirderRunning())
  {
    QString msg = QString("Cumulus server NOT FOUND at %1").arg(cumulusUrl);
    QMessageBox::critical(NULL, QObject::tr("Cumulus Server Not Found"), msg);
    return;
  }

  // Open NERSC login dialog
  pqNerscLoginBehavior* loginBehavior = pqNerscLoginBehavior::instance();
  loginBehavior->startLogin();
}

void pqCumulusJobsPanel::requestSimulationResults()
{
  QAction* action = qobject_cast<QAction*>(sender());
  if (!action)
  {
    QMessageBox::critical(this, "Error", "Not connected to QAction?");
    return;
  }

  cumulus::Job job = action->data().value<cumulus::Job>();
  // QString message;
  // QTextStream qs(&message);
  // qs << "The slot named test() was called for job " << job.id() << ".\n\n"
  //    << "Download folder: " << job.downloadFolder();
  // QMessageBox::information(this, "Test", message);

  emit this->loadSimulationResults(job.downloadFolder());
}
