//=========================================================================
//  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/pqACE3PNewProjectBehavior.h"
#include "plugin/pqACE3PJobsBehavior.h"
#include "plugin/pqACE3PStagesBehavior.h"

// Local includes
#include "smtk/simulation/ace3p/Metadata.h"
#include "smtk/simulation/ace3p/operations/Create.h"
#include "smtk/simulation/ace3p/qt/qtNewProjectWizard.h"
#include "smtk/simulation/ace3p/qt/qtProjectRuntime.h"
// #include "smtk/simulation/ace3p/utility/ModelUtils.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/pqSMTKBehavior.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
#include "smtk/extension/qt/qtOperationDialog.h"
#include "smtk/operation/Manager.h"
#include "smtk/operation/Operation.h"
#include "smtk/project/Project.h"

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

// VTK
#include <vtk_jsoncpp.h> // for Json::Value

#include <QDebug>
#include <QDockWidget>
#include <QFileInfo>
#include <QMessageBox>
#include <QPushButton>
#include <QSharedPointer>

#include <cassert>
#include <sstream>
#include <string>
#include <vector>

namespace
{
const std::string SurfacePaletteName = "Brewer Qualitative Dark2";
const std::string VolumePaletteName = "Brewer Qualitative Pastel2";

// Constants that match the NewProjectLocationMode enum in ACE3PSettings.xml
const int ProjectLocationUsePrevious = 0;
const int ProjectLocationUseCWD = 1;

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

class pqACE3PNewProjectBehaviorInternals
{
public:
  pqACE3PNewProjectBehaviorInternals(QWidget* parent)
  {
    m_wizard = new smtk::simulation::ace3p::qtNewProjectWizard(parent);

    // this->loadPalette(m_surfacePalette, SurfacePaletteName);
    // this->loadPalette(m_volumePalette, VolumePaletteName);
  }
  ~pqACE3PNewProjectBehaviorInternals() = default;

  // void assignColors(smtk::model::ResourcePtr modelResource) const
  // {
  //   m_modelUtils.assignColors(modelResource, smtk::model::FACE, m_surfacePalette);
  //   m_modelUtils.assignColors(modelResource, smtk::model::VOLUME, m_volumePalette);
  // }

  smtk::simulation::ace3p::qtNewProjectWizard* m_wizard = nullptr;

protected:
  // smtk::simulation::ace3p::ModelUtils m_modelUtils;
  std::vector<std::string> m_surfacePalette;
  std::vector<std::string> m_volumePalette;

  // void loadPalette(std::vector<std::string>& palette, const std::string& paletteName)
  // {
  //   // Grab color palettes from pqPresetDialog
  //   pqPresetDialog dialog;
  //   dialog.setCurrentPreset(paletteName.c_str());
  //
  //   const Json::Value preset = dialog.currentPreset();
  //   assert(preset.isMember("IndexedColors"));
  //
  //   palette.clear();
  //   const Json::Value& jsonColors(preset["IndexedColors"]);
  //   Json::ArrayIndex numColors = jsonColors.size() / 3;
  //   for (Json::ArrayIndex cc = 0; cc < numColors; ++cc)
  //   {
  //     std::ostringstream colorStr;
  //     colorStr << "#";
  //     for (int cm = 0; cm < 3; ++cm)
  //     {
  //       int val = static_cast<int>(jsonColors[3 * cc + cm].asDouble() * 255.0);
  //       colorStr << std::setfill('0') << std::setw(2) << std::hex << val;
  //     }
  //     palette.push_back(colorStr.str());
  //   }
  // }
};

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

//-----------------------------------------------------------------------------
void pqACE3PNewProjectReaction::onTriggered()
{
  pqACE3PNewProjectBehavior::instance()->newProject();
}

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

//-----------------------------------------------------------------------------
pqACE3PNewProjectBehavior::pqACE3PNewProjectBehavior(QObject* parent)
  : Superclass(parent)
{
  this->Internals = new pqACE3PNewProjectBehaviorInternals(pqCoreUtilities::mainWidget());
}

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

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

  return g_instance;
}

//-----------------------------------------------------------------------------
pqACE3PNewProjectBehavior::~pqACE3PNewProjectBehavior()
{
  if (g_instance == this)
  {
    delete this->Internals;
    g_instance = nullptr;
  }

  QObject::disconnect(this);
}

//-----------------------------------------------------------------------------
void pqACE3PNewProjectBehavior::newProject()
{
  // Todo abort if qtProjectRuntime project is already set.
  auto* runtime = smtk::simulation::ace3p::qtProjectRuntime::instance();
  if (runtime->project() != nullptr)
  {
    qWarning() << "Must close current project first.";
    return;
  }

  // Access the active server
  pqServer* server = pqActiveObjects::instance().activeServer();
  pqSMTKWrapper* wrapper = pqSMTKBehavior::instance()->resourceManagerForServer(server);

  QWidget* mainWidget = pqCoreUtilities::mainWidget();

  // Get the "new project" settings
  QString startLocation;     // initial project location
  QString meshFileDirectory; // initial directory for selecting mesh file

  vtkSMProxy* proxy = server->proxyManager()->GetProxy("settings", "ACE3PSettings");
  if (!proxy)
  {
    QMessageBox::warning(
      mainWidget, "Internal Error", "Internal Error: Settings proxy for ACE3P not found.");
    return;
  }

  // Initial project location
  vtkSMStringVectorProperty* projectLocationProperty = nullptr;
  vtkSMProperty* modeProp = proxy->GetProperty("NewProjectLocationMode");
  auto* intModeProp = vtkSMIntVectorProperty::SafeDownCast(modeProp);
  int locationMode = intModeProp->GetElement(0);
  if (locationMode == ProjectLocationUsePrevious)
  {
    vtkSMProperty* projectProp = proxy->GetProperty("LastProjectLocation");
    projectLocationProperty = vtkSMStringVectorProperty::SafeDownCast(projectProp);
    QString lastProjectLocation = projectLocationProperty->GetElement(0);
    startLocation = lastProjectLocation.isEmpty() ? QDir::homePath() + "/modelbuilder/projects"
                                                  : lastProjectLocation;
  }
  else if (locationMode == ProjectLocationUseCWD)
  {
    startLocation = QDir::currentPath();
  }

  // Initial mesh file directory
  vtkSMStringVectorProperty* meshFileDirectoryProperty = nullptr;
  vtkSMProperty* meshDirectoryProp = proxy->GetProperty("LastMeshFileDirectory");
  meshFileDirectoryProperty = vtkSMStringVectorProperty::SafeDownCast(meshDirectoryProp);
  QString stringMeshDirectory = meshFileDirectoryProperty->GetElement(0);
  meshFileDirectory = stringMeshDirectory.isEmpty() ? QDir::homePath() : stringMeshDirectory;

  auto projManager = wrapper->smtkProjectManager();
  this->Internals->m_wizard->restart();
  this->Internals->m_wizard->setProjectManager(projManager);
  this->Internals->m_wizard->setProjectLocation(startLocation);
  this->Internals->m_wizard->setMeshFileDirectory(meshFileDirectory);
  int result = this->Internals->m_wizard->exec();
  if (result != QDialog::Accepted)
  {
    return;
  }

  // Notify that project was created
  auto project = this->Internals->m_wizard->project();
  runtime->setProject(project);
  emit this->projectCreated(project);

  // Find AttributeEditor widgets and raise to front
  QDockWidget* attEditor = mainWidget->findChild<QDockWidget*>("pqSMTKAttributePanel");
  if (attEditor == nullptr)
  {
    qWarning() << "Unable to find Attribute Editor";
  }
  else
  {
    attEditor->show();
    attEditor->raise();
  }

  // Save the "last used" settings
  projectLocationProperty->SetElement(
    0, this->Internals->m_wizard->projectLocation().toStdString().c_str());
  meshFileDirectoryProperty->SetElement(
    0, this->Internals->m_wizard->meshFileDirectory().toStdString().c_str());
  proxy->UpdateVTKObjects();

  // set visibility of the Stages panel based on number of Stages present
  pqACE3PStagesBehavior::instance()->updateStagesPanelVisibility(project);

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

  return;
} // newProject()
