//=========================================================================
//  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 "pqNerscLoginBehavior.h"

#include "smtk/newt/qtNewtInterface.h"
#include "smtk/newt/qtNewtLoginDialog.h"

#include "pqCoreUtilities.h"

#include <QDebug>
#include <QMessageBox>
#include <QNetworkReply>
#include <QSslSocket>
#include <QStyle>
#include <QTimer>

// Use #define to bypass interactive login - for development only
// Uses file at ~/.newt_sessionid to provide NEWT session id
#ifndef NDEBUG
#define MOCK_LOGIN 0
#if MOCK_LOGIN
#include <QDir>
#include <QFile>
#include <QTextStream>
#endif
#endif

//-----------------------------------------------------------------------------
pqNerscLoginReaction::pqNerscLoginReaction(QAction* parentObject)
  : Superclass(parentObject)
{
  pqNerscLoginBehavior* behavior = pqNerscLoginBehavior::instance();
  behavior->setMenuAction(parentObject);
}

void pqNerscLoginReaction::onTriggered()
{
  pqNerscLoginBehavior* behavior = pqNerscLoginBehavior::instance();
  behavior->startLogin();
}

//-----------------------------------------------------------------------------
static pqNerscLoginBehavior* g_instance = nullptr;

pqNerscLoginBehavior::pqNerscLoginBehavior(QObject* parent)
  : Superclass(parent)
  , m_isLoggedIn(false)
  , m_action(nullptr)
  , m_loginDialog(nullptr)
{
  m_loginDialog = new newt::qtNewtLoginDialog(pqCoreUtilities::mainWidget());
  QObject::connect(
    m_loginDialog,
    &newt::qtNewtLoginDialog::entered,
    this,
    &pqNerscLoginBehavior::onLoginCredentials);

  auto newtInterface = newt::qtNewtInterface::instance();
  QObject::connect(
    newtInterface,
    &newt::qtNewtInterface::loginComplete,
    this,
    &pqNerscLoginBehavior::onLoginComplete);
  QObject::connect(
    newtInterface, &newt::qtNewtInterface::error, this, &pqNerscLoginBehavior::onLoginError);
  QObject::connect(
    newtInterface, &newt::qtNewtInterface::loginFail, this, &pqNerscLoginBehavior::onLoginFail);
}

pqNerscLoginBehavior* pqNerscLoginBehavior::instance(QObject* parent)
{
  if (!g_instance)
  {
    g_instance = new pqNerscLoginBehavior(parent);
  }

  if (g_instance->parent() == nullptr && parent)
  {
    g_instance->setParent(parent);
  }

  return g_instance;
}

pqNerscLoginBehavior::~pqNerscLoginBehavior()
{
  if (g_instance == this)
  {
    g_instance = nullptr;
  }

  QObject::disconnect(this);
}

void pqNerscLoginBehavior::startLogin()
{
  if (m_isLoggedIn)
  {
    return;
  }

  // Check for SSL support
  if (!QSslSocket::supportsSsl())
  {
    QWidget* parentWidget = pqCoreUtilities::mainWidget();
    QMessageBox::critical(
      parentWidget,
      "Missing SSL Support",
      "Cannot connect to NERSC because OpenSSL is missing"
      " or is an unsupported version.");
    return;
  }

#if MOCK_LOGIN
  // For testing, uses session id stored in users home dir
  QString path = QDir::homePath() + QString("/.newt_sessionid");
  QFile file(path);
  if (!file.open(QIODevice::ReadOnly))
  {
    QWidget* parentWidget = pqCoreUtilities::mainWidget();
    QMessageBox::critical(
      parentWidget,
      "Bypass Login Error",
      "Cannot bypass login because file ~/.newt_sessionid not found");
    return;
  }
  QTextStream qs(&file);
  QString newtSessionId = qs.readAll().trimmed();

  newt::qtNewtInterface* newtInterface = newt::qtNewtInterface::instance();
  newtInterface->mockLogin("johnt", newtSessionId);
  return;
#endif

  // Configure geometry to center on main widget.
  // Do this instead of setting parent widget, to prevent crash when
  // the application exits (main window destructor).
  m_loginDialog->adjustSize();
  m_loginDialog->setGeometry(QStyle::alignedRect(
    Qt::LeftToRight,
    Qt::AlignCenter,
    m_loginDialog->size(),
    pqCoreUtilities::mainWidget()->geometry()));
  m_loginDialog->show();
}

void pqNerscLoginBehavior::onLoginCredentials(const QString userName, const QString password)
{
  newt::qtNewtInterface* newtInterface = newt::qtNewtInterface::instance();
  newtInterface->login(userName, password);
}

void pqNerscLoginBehavior::onLoginComplete(const QString userName)
{
  m_isLoggedIn = true; // ignore further menu requests
  QString text = QString("Logged in to NERSC as ") + userName;

  // Update menu text
  if (m_action != nullptr)
  {
    m_action->setText(text);
  }

#if MOCK_LOGIN
#else
  QWidget* parentWidget = pqCoreUtilities::mainWidget();
  QMessageBox::information(parentWidget, "NERSC Login Complete", text);
#endif

  QObject::disconnect(this);
}

void pqNerscLoginBehavior::onLoginError(const QString message)
{
  // Login failures sometimes cause 500 errors
  // So put incoming message in details section
  QWidget* parentWidget = pqCoreUtilities::mainWidget();
  QMessageBox msgBox(
    QMessageBox::Warning,
    "NERSC Login Failed",
    "The logic attempt failed.",
    QMessageBox::Close,
    parentWidget);
  msgBox.exec();
}

void pqNerscLoginBehavior::onLoginFail(const QString message)
{
  QWidget* parentWidget = pqCoreUtilities::mainWidget();
  QMessageBox::information(parentWidget, "NERSC Login Failed", message);
}
