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

#include "smtk/simulation/windtunnel/Metadata.h"
#include "smtk/simulation/windtunnel/qt/qtSessionData.h"
// #include "smtk/simulation/windtunnel/qt/qtTaskWidget.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/pqSMTKAttributeDock.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKBehavior.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
#include "smtk/extension/qt/qtBaseView.h"
#include "smtk/extension/qt/qtUIManager.h"
#include "smtk/io/Logger.h"
#include "smtk/operation/Manager.h"
#include "smtk/project/operators/Read.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 <QFile>
#include <QMessageBox>
#include <QString>
#include <QTextStream>

#include <nlohmann/json.hpp>

namespace wind = smtk::simulation::windtunnel;

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

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

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

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

  return g_instance;
}

//-----------------------------------------------------------------------------
bool pqWindTunnelProjectLoader::canLoad(const pqServerResource& resource) const
{
  // Check for tag set to this project type
  QString tag = resource.data(RECENTLY_USED_PROJECTS_TAG, "");
  return tag.toStdString() == smtk::simulation::windtunnel::Metadata::PROJECT_TYPE;
}

//-----------------------------------------------------------------------------
bool pqWindTunnelProjectLoader::load(pqServer* server, const QString& directoryPath)
{
  auto* session = smtk::simulation::windtunnel::qtSessionData::instance();
  if (session->project() != nullptr)
  {
    qWarning() << "Must close current project first.";
    return false;
  }

  // Find the project file
  QDir projectDir(directoryPath);
  QString filename = projectDir.dirName() + wind::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::project::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;
  }

  // Load TaskFlow data
  // std::shared_ptr<wind::TaskFlow> taskFlow(new wind::TaskFlow);
  // filename = "taskflow.json";
  // if (!projectDir.exists(filename))
  // {
  //   qWarning() << "Project missing" << filename;
  // }
  // else
  // {
  //   QString content;
  //   QString path = projectDir.absoluteFilePath(filename);
  //   QFile file(path);
  //   if (file.open(QFile::ReadOnly | QFile::Text))
  //   {
  //     QTextStream in(&file);
  //     content = in.readAll();
  //     nlohmann::json j = nlohmann::json::parse(content.toStdString());
  //     taskFlow->readFromJson(j);
  //     file.close();
  //   }
  // }

  // Add project to recently-used list
  pqServerResource serverResource = server->getResource();
  QString tag = QString::fromStdString(smtk::simulation::windtunnel::Metadata::PROJECT_TYPE);
  serverResource.addData(RECENTLY_USED_PROJECTS_TAG, tag);
  serverResource.setPath(directoryPath);

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

  smtk::attribute::ResourceItemPtr projectItem = result->findResource("resourcesCreated");
  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__;
  }

  session->setProject(project);

  // Find the Attribute Editor dock widget and raise to front
  QWidget* mainWidget = pqCoreUtilities::mainWidget();
  auto* attributeDock = mainWidget->findChild<pqSMTKAttributeDock*>("pqSMTKAttributeDock");
  if (attributeDock == nullptr)
  {
    qWarning() << "Unable to find Attribute Editor (pqSMTKAttributeDock)";
  }
  else
  {
    // A bit klugey, but force attribute editor to rebuild after session project is set.
    // This is needed because control views are hidden if there is no project, and
    // the attribute editor is loaded before the session project is set.
    QWidget* widget = attributeDock->widget();
    pqSMTKAttributePanel* attrPanel = dynamic_cast<pqSMTKAttributePanel*>(widget);
    if (attrPanel != nullptr)
    {
      auto* uiManager = attrPanel->attributeUIManager();
      if (uiManager != nullptr)
      {
        uiManager->topView()->updateUI();
      }
    }

    attributeDock->show();
    attributeDock->raise();
  }

  // set the Project name in the Title Bar
  QString windowTitle = mainWidget->windowTitle();
  mainWidget->setWindowTitle(windowTitle + " -- Project: " + QString(project->name().c_str()));

  qInfo() << "Opened project" << project->name().c_str();
  // Q_EMIT this->projectOpened(project, taskFLow);
  Q_EMIT this->projectOpened(project);

  return true;
}

//-----------------------------------------------------------------------------
bool pqWindTunnelProjectLoader::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;
}

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

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

  QObject::disconnect(this);
}
