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

// Local includes
#include "smtk/simulation/windtunnel/Metadata.h"
#include "smtk/simulation/windtunnel/qt/qtSessionData.h"

// SMTK
#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/attribute/VoidItem.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/qtOperationDialog.h"
#include "smtk/extension/qt/qtUIManager.h"
#include "smtk/operation/Manager.h"
#include "smtk/operation/Operation.h"
#include "smtk/project/Project.h"

// ParaView
#include "pqActiveObjects.h"
#include "pqApplicationCore.h"
#include "pqCoreUtilities.h"
#include "pqFileDialog.h"
#include "pqPresetDialog.h"
#include "pqRecentlyUsedResourcesList.h"
#include "pqServer.h"
#include "pqServerResource.h"
#include "vtkSMIntVectorProperty.h"
#include "vtkSMProperty.h"
#include "vtkSMProxy.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMStringVectorProperty.h"

// Qt
#include <QDebug>
#include <QDir>
#include <QDockWidget>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QPushButton>
#include <QSharedPointer>
#include <QString>
#include <QTextStream>

#include <nlohmann/json.hpp>

#include <string>
#include <vector>

namespace
{
const std::string OP_NAME = "create_project.CreateProject";
const int OP_SUCCEEDED = static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED);

nlohmann::json readJsonFile(const QString& path)
{
  QFile file(path);
  if (file.open(QFile::ReadOnly | QFile::Text))
  {
    QTextStream in(&file);
    QString content = in.readAll();
    nlohmann::json j = nlohmann::json::parse(content.toStdString());
    file.close();
    return j;
  }

  // (else)
  qWarning() << "Failed to open file" << path;
  return nlohmann::json();
}

} // namespace

//-----------------------------------------------------------------------------
pqWindTunnelNewProjectReaction::pqWindTunnelNewProjectReaction(QAction* parentObject)
  : Superclass(parentObject)
{
}

//-----------------------------------------------------------------------------
void pqWindTunnelNewProjectReaction::onTriggered()
{
  pqWindTunnelNewProjectBehavior::instance()->newProject();
}

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

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

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

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

  return g_instance;
}

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

  QObject::disconnect(this);
}

//-----------------------------------------------------------------------------
void pqWindTunnelNewProjectBehavior::newProject()
{
  auto* session = smtk::simulation::windtunnel::qtSessionData::instance();
  if (session->project() != nullptr)
  {
    qWarning() << "Must close current project first.";
    return;
  }

  // Instantiate and run the operation
  pqServer* server = pqActiveObjects::instance().activeServer();
  pqSMTKWrapper* wrapper = pqSMTKBehavior::instance()->resourceManagerForServer(server);

  QWidget* mainWidget = pqCoreUtilities::mainWidget();

  auto opManager = wrapper->smtkOperationManager();
  smtk::operation::OperationPtr op = opManager->create(OP_NAME);

  // Construct a modal dialog for the operation spec
  QSharedPointer<smtk::extension::qtOperationDialog> dialog =
    QSharedPointer<smtk::extension::qtOperationDialog>(new smtk::extension::qtOperationDialog(
      op, wrapper->smtkResourceManager(), wrapper->smtkViewManager(), mainWidget));
  dialog->setObjectName("NewProjectDialog");
  dialog->setWindowTitle("New Project");
  QObject::connect(
    dialog.get(),
    &smtk::extension::qtOperationDialog::operationExecuted,
    this,
    &pqWindTunnelNewProjectBehavior::onOperationExecuted);
  dialog->exec();
}

void pqWindTunnelNewProjectBehavior::onOperationExecuted(
  const smtk::operation::Operation::Result& result)
{
  if (result->findInt("outcome")->value() != OP_SUCCEEDED)
  {
    return;
  }

  // Get the new project instance
  auto resource = result->findResource("resourcesCreated")->value();
  auto project = std::dynamic_pointer_cast<smtk::project::Project>(resource);
  if (project == nullptr)
  {
    qCritical() << "Internal Error: project not found" << __FILE__ << __LINE__;
    return;
  }

  // Update session data
  auto* session = smtk::simulation::windtunnel::qtSessionData::instance();
  session->setProject(project);

  Q_EMIT this->projectCreated(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 && attrPanel->attributeUIManager())
    {
      attrPanel->attributeUIManager()->topView()->updateUI();
    }

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

  // Add project to recently-used list
  pqServer* server = pqActiveObjects::instance().activeServer();
  pqServerResource serverResource = server->getResource();

  // Add string for recently used list
  QString value = QString::fromStdString(smtk::simulation::windtunnel::Metadata::PROJECT_TYPE);
  serverResource.addData(RECENTLY_USED_PROJECTS_TAG, value);

  QString smtkLocation = QString::fromStdString(project->location());
  QFileInfo smtkInfo(smtkLocation);
  QDir projectDir = smtkInfo.dir();
  serverResource.setPath(projectDir.absolutePath());

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

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

  return;
} // newProject()
