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

#include "plugin/pqACE3PJobsBehavior.h"
#include "plugin/pqACE3PStagesBehavior.h"
#include "smtk/simulation/ace3p/Metadata.h"
#include "smtk/simulation/ace3p/operations/Read.h"
#include "smtk/simulation/ace3p/qt/qtProjectRuntime.h"
#include "smtk/simulation/ace3p/qt/qtStagesWidget.h"

// SMTK
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKBehavior.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
#include "smtk/io/Logger.h"
#include "smtk/operation/Manager.h"

// ParaView (client)
#include "pqApplicationCore.h"
#include "pqCoreUtilities.h"
#include "pqRecentlyUsedResourcesList.h"
#include "pqServer.h"
#include "pqServerConfiguration.h"
#include "pqServerConnectDialog.h"
#include "pqServerLauncher.h"
#include "pqServerManagerModel.h"
#include "pqServerResource.h"

// Qt
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QString>
#include <QTextStream>

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

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

//-----------------------------------------------------------------------------
pqACE3PProjectLoader* pqACE3PProjectLoader::instance(QObject* parent)
{
  if (!g_instance)
  {
    g_instance = new pqACE3PProjectLoader(parent);
  }

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

  return g_instance;
}

//-----------------------------------------------------------------------------
bool pqACE3PProjectLoader::canLoad(const pqServerResource& resource) const
{
  return resource.hasData(RECENTLY_USED_PROJECTS_TAG);
}

//-----------------------------------------------------------------------------
bool pqACE3PProjectLoader::load(pqServer* server, const QString& directoryPath)
{
  // Todo abort if qtProjectRuntime project is already set.
  // Find the project file
  QDir projectDir(directoryPath);
  QString filename =
    projectDir.dirName() + smtk::simulation::ace3p::Metadata::PROJECT_FILE_EXTENSION.c_str();
  if (!projectDir.exists(filename))
  {
    QString msg;
    QTextStream qs(&msg);
    qs << "ERROR: project file (" << filename << ") not found in project folder ("
       << projectDir.canonicalPath() << ").";
    QMessageBox::warning(pqCoreUtilities::mainWidget(), "Error", msg);
    return false;
  }

  // Set up project read operation
  pqSMTKWrapper* wrapper = pqSMTKBehavior::instance()->resourceManagerForServer(server);
  auto opManager = wrapper->smtkOperationManager();

  auto readOp = opManager->create<smtk::simulation::ace3p::Read>();
  assert(readOp != nullptr);

  QString path = projectDir.absoluteFilePath(filename);
  readOp->parameters()->findFile("filename")->setValue(path.toStdString());

  auto result = readOp->operate();
  int outcome = result->findInt("outcome")->value();
  if (outcome != OP_SUCCEEDED)
  {
    qWarning() << readOp->log().convertToString().c_str();
    QString msg =
      "There was an error loading the project. Check the Output Messages view for more info.";
    QMessageBox::warning(pqCoreUtilities::mainWidget(), "Error", msg);
    return false;
  }

  // Add project to recently-used list
  pqServerResource serverResource = server->getResource();
  serverResource.addData(RECENTLY_USED_PROJECTS_TAG, "1");
  serverResource.setPath(directoryPath);

  pqRecentlyUsedResourcesList& mruList = pqApplicationCore::instance()->recentlyUsedResources();
  mruList.add(serverResource);
  mruList.save(*pqApplicationCore::instance()->settings());

  smtk::attribute::ResourceItemPtr projectItem = result->findResource("resource");
  auto resource = projectItem->value();
  // std::cout << "Created resource type " << resource->typeName() << std::endl;
  auto project = std::dynamic_pointer_cast<smtk::project::Project>(resource);
  if (!project)
  {
    qWarning() << "Internal Error - project was not loaded" << __FILE__ << __LINE__;
  }

  smtk::simulation::ace3p::qtProjectRuntime::instance()->setProject(project);

  // set visibility of the Stages panel based on number of Stages present
  auto ace3pProject = std::dynamic_pointer_cast<smtk::simulation::ace3p::Project>(project);
  pqACE3PStagesBehavior::instance()->updateStagesPanelVisibility(ace3pProject);

  // set the Jobs panel visible
  pqACE3PJobsBehavior::instance()->setPanelVisible(true);

  qInfo() << "Opened project" << project->name().c_str();
  emit this->projectOpened(project);

  return true;
}

//-----------------------------------------------------------------------------
bool pqACE3PProjectLoader::load(const pqServerResource& resource)
{
  const pqServerResource server = resource.schemeHostsPorts();

  pqServerManagerModel* smModel = pqApplicationCore::instance()->getServerManagerModel();
  pqServer* pq_server = smModel->findServer(server);
  if (!pq_server)
  {
    int ret = QMessageBox::warning(
      pqCoreUtilities::mainWidget(),
      tr("Disconnect from current server?"),
      tr("The file you opened requires connecting to a new server. \n"
         "The current connection will be closed.\n\n"
         "Are you sure you want to continue?"),
      QMessageBox::Yes | QMessageBox::No);
    if (ret == QMessageBox::No)
    {
      return false;
    }
    pqServerConfiguration config_to_connect;
    if (pqServerConnectDialog::selectServer(
          config_to_connect, pqCoreUtilities::mainWidget(), server))
    {
      QScopedPointer<pqServerLauncher> launcher(pqServerLauncher::newInstance(config_to_connect));
      if (launcher->connectToServer())
      {
        pq_server = launcher->connectedServer();
      }
    }
  }

  if (pq_server)
  {
    return this->load(pq_server, resource.path());
  }

  // (else)
  return false;
}

//-----------------------------------------------------------------------------
pqACE3PProjectLoader::pqACE3PProjectLoader(QObject* parent)
  : Superclass(parent)
{
}

//-----------------------------------------------------------------------------
pqACE3PProjectLoader::~pqACE3PProjectLoader()
{
  if (g_instance == this)
  {
    g_instance = nullptr;
  }

  QObject::disconnect(this);
}
