//=========================================================================
//  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 "smtk/simulation/ace3p/qt/qtNewProjectOperatePage.h"

#include "smtk/simulation/ace3p/Project.h"
#include "smtk/simulation/ace3p/operations/Create.h"
#include "smtk/simulation/ace3p/operations/Write.h"
#include "smtk/simulation/ace3p/qt/qtOperationLauncher.h"

// SMTK includes
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/DirectoryItem.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/operation/Manager.h"
#include "smtk/project/Manager.h"

#include <QCoreApplication>
#include <QDebug>
#include <QFlags>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QProgressBar>
#include <QString>
#include <QTextStream>
#include <QTimer>
#include <QVBoxLayout>
#include <QVariant>

namespace
{
const int OP_SUCCEEDED = static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED);
} // namespace

namespace smtk
{
namespace simulation
{
namespace ace3p
{

qtNewProjectOperatePage::qtNewProjectOperatePage(QWidget* parent)
  : Superclass(parent)
  , m_launcher(new qtOperationLauncher)
  , m_listWidget(new QListWidget)
{
  this->setTitle("Creating Project");

  auto* layout = new QVBoxLayout();
  auto* progressBar = new QProgressBar(this);
  progressBar->setRange(0, 0);
  layout->addWidget(progressBar);

  layout->addWidget(m_listWidget);
  this->setLayout(layout);
}

qtNewProjectOperatePage::~qtNewProjectOperatePage()
{
  delete m_launcher;
}

bool qtNewProjectOperatePage::operate()
{
  m_listWidget->clear();
  m_listWidget->addItem("Creating resources...");

  // Gather inputs
  QString location = this->field("project.location").toString();
  QString name = this->field("project.name").toString();
  QString meshfile = this->field("project.meshfile").toString();

  // Instantiate the Create operator
  auto projManager = m_projectManager.lock();
  auto opManager = projManager->operationManager();
  auto createOp = opManager->create<Create>();
  assert(createOp != nullptr);

  bool ok;
  QString projectPath = location + "/" + name;
  ok = createOp->parameters()->findDirectory("location")->setValue(projectPath.toStdString());
  assert(ok);
  ok = createOp->parameters()->findFile("analysis-mesh")->setValue(meshfile.toStdString());
  assert(ok);

  // Execute operation after the connection has been made and return future
  smtk::io::Logger::instance().reset();
  std::shared_ptr<ResultHandler> handler = (*m_launcher)(createOp);
  QObject::connect(
    handler.get(), &ResultHandler::resultReady, this, &qtNewProjectOperatePage::onCreateResult);
  return true;
}

void qtNewProjectOperatePage::onCreateResult(smtk::operation::Operation::Result createResult)
{
  if (!this->checkResult(createResult, "Create"))
  {
    this->wizard()->reject();
    return;
  }
  // qDebug() << "Create operation finished.";
  int last = m_listWidget->count() - 1;
  if (last >= 0)
  {
    auto* item = m_listWidget->item(last);
    QString text = item->text() + "  Done";
    item->setText(text);
  }

  auto resItem = createResult->findResource("resource");
  auto resource = resItem->value();
  m_project = std::dynamic_pointer_cast<smtk::simulation::ace3p::Project>(resource);

  // Write the project
  m_listWidget->addItem("Writing project files...");

  auto projManager = m_projectManager.lock();
  auto opManager = projManager->operationManager();
  auto writeOp = opManager->create<Write>();
  assert(writeOp != nullptr);
  writeOp->parameters()->associate(m_project);

  // Execute the operation and connect to the future
  std::shared_ptr<ResultHandler> handler = (*m_launcher)(writeOp);
  QObject::connect(
    handler.get(), &ResultHandler::resultReady, this, &qtNewProjectOperatePage::onWriteResult);
}

void qtNewProjectOperatePage::onWriteResult(smtk::operation::Operation::Result writeResult)
{
  if (!this->checkResult(writeResult, "Write"))
  {
    this->wizard()->reject();
    return;
  }
  // qDebug() << "Write operation finished.";
  int last = m_listWidget->count() - 1;
  if (last >= 0)
  {
    auto* item = m_listWidget->item(last);
    QString text = item->text() + " Done";
    item->setText(text);
  }

  // Advance to the next wizard page
  // Insert delay to for user feedback
  QCoreApplication::processEvents();
  QTimer::singleShot(300, [this]() { this->wizard()->next(); });
  // this->wizard()->next();
}

bool qtNewProjectOperatePage::checkResult(
  const smtk::operation::Operation::Result& result,
  const QString& operationName)
{
  int outcome = result->findInt("outcome")->value();
  if (outcome == OP_SUCCEEDED)
  {
    return true;
  }

  // (else)
  QString title = "Operation Failed";

  QString msg;
  QTextStream qsMsg(&msg);
  qsMsg << "The " << operationName << " operation failed, with outcome: " << outcome;

  QMessageBox::StandardButtons flags(QMessageBox::Close);
  QMessageBox msgBox(QMessageBox::Critical, title, msg, flags, this);
  const smtk::io::Logger& log = smtk::io::Logger::instance();
  if (log.numberOfRecords() > 0)
  {
    QString details = QString::fromStdString(log.convertToString());
    msgBox.setDetailedText(details);
  }
  // resize() doesn't work on msgBox so instead use css
  msgBox.setStyleSheet("QLabel{width:640 px;}");
  msgBox.exec();

  return false;
}

} // namespace ace3p
} // namespace simulation
} // namespace smtk
