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

// SMTK includes
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/Resource.h"

namespace smtk
{
namespace simulation
{
namespace ace3p
{

std::shared_ptr<ResultHandler> qtOperationLauncher::operator()(
  const smtk::operation::Operation::Ptr& op)
{
  // Create Result Handler
  std::shared_ptr<ResultHandler> handler = std::make_shared<ResultHandler>();

  // Construct a copy of the operation shared pointer to pass to the subsequent
  // lambda to prevent the underlying operation from being changed before the
  // operation completes.
  const smtk::operation::Operation::Ptr& operation = op;

  // When the subthread completes the operation, it emits a private signal
  // containing the name of the result. We pass the result name rather than the
  // result itself because QStrings are designed to pass from signals to slots
  // across threads without registration with qMetaData. Given this class
  // instance as a context, we associate the subthread's signal with a lambda
  // that accesses the result of the operation and emits it on the thread
  // associated with this object. Each connection we make is a one-shot
  // associated to the input operation, so we delete the connection upon firing.
  QMetaObject::Connection* connection = new QMetaObject::Connection;
  *connection = QObject::connect(
    this,
    &qtOperationLauncher::operationHasResult,
    this,
    [&, connection, operation, handler](QString parametersName, QString resultName) {
      if (parametersName.toStdString() == operation->parameters()->name())
      {
        auto result = operation->specification()->findAttribute(resultName.toStdString());
        emit handler->resultReady(result);

        // Remove this connection.
        QObject::disconnect(*connection);
        delete connection;
      }
    });

  handler->m_future = m_threadPool(&qtOperationLauncher::run, this, op);

  // Return the future associated with the promise created above.
  return handler;
}

smtk::operation::Operation::Result qtOperationLauncher::run(
  smtk::operation::Operation::Ptr operation)
{
  // Execute the operation in the subthread.
  auto result = operation->operate();

  // Privately emit the name of the output result so the contents of this class
  // that reside on the original thread can access it.
  emit operationHasResult(
    QString::fromStdString(operation->parameters()->name()),
    QString::fromStdString(result->name()),
    QPrivateSignal());

  return result;
}

ResultHandler::ResultHandler()
  : QObject(nullptr)
{
}

smtk::operation::Operation::Result ResultHandler::waitForResult()
{
  return m_future.get();
}

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