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

#include "pqActiveObjects.h"
#include "pqApplicationCore.h"
#include "pqCoreUtilities.h"

#include "smtk/simulation/ace3p/operations/RemoveStage.h"
#include "smtk/simulation/ace3p/qt/qtProjectRuntime.h"

#include "plugin/pqACE3PCloseBehavior.h"
#include "plugin/pqACE3PJobsBehavior.h"
#include "plugin/pqACE3PNewProjectBehavior.h"
#include "plugin/pqACE3PNewStageBehavior.h"
#include "plugin/pqACE3PProjectLoader.h"
#include "plugin/pqACE3PStagesBehavior.h"

#include "smtk/simulation/ace3p/qt/qtJobsWidget.h"
#include "smtk/simulation/ace3p/qt/qtStagesWidget.h"

#include "smtk/extension/paraview/appcomponents/pqSMTKBehavior.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"

#include "smtk/attribute/IntItem.h"

#include <QDebug>
#include <QMessageBox>

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

// Macro for handling internal errors
#define InternalCheckMacro(condition, msg)                                                         \
  do                                                                                               \
  {                                                                                                \
    if (!(condition))                                                                              \
    {                                                                                              \
      QMessageBox::critical(pqCoreUtilities::mainWidget(), "Internal Error", msg);                 \
      return;                                                                                      \
    }                                                                                              \
  } while (0)
} // namespace

pqACE3PStagesPanel::pqACE3PStagesPanel(QWidget* parent)
  : QDockWidget(parent)
  , m_stageWidget(nullptr)
{
  this->setObjectName("Stages Panel");
  this->setWindowTitle("Analysis Stages");

  m_stageWidget = new smtk::simulation::ace3p::qtStagesWidget(parent);
  m_stageWidget->setObjectName("stages_panel_widget");

  this->setWidget(m_stageWidget);

  auto newBehavior = pqACE3PNewProjectBehavior::instance();
  auto loader = pqACE3PProjectLoader::instance();
  auto closeBehavior = pqACE3PCloseBehavior::instance();
  auto newStageBehavior = pqACE3PNewStageBehavior::instance();
  auto stagesBehavior = pqACE3PStagesBehavior::instance();
  auto jobsBehavior = pqACE3PJobsBehavior::instance();

  stagesBehavior->setStagesPanelPointer(this);

  // pass pqACE3P signals to qtStagesWidget
  QObject::connect(
    newBehavior,
    &pqACE3PNewProjectBehavior::projectCreated,
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::setProject);
  QObject::connect(
    loader,
    &pqACE3PProjectLoader::projectOpened,
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::setProject);
  QObject::connect(
    closeBehavior,
    &pqACE3PCloseBehavior::projectClosed,
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::unsetProject);
  QObject::connect(
    newStageBehavior,
    &pqACE3PNewStageBehavior::stageAdded,
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::onStageAdded);
  QObject::connect(
    this,
    &pqACE3PStagesPanel::updateCurrentStage,
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::onUpdateProjectCurrentStage);

  // connection to the Jobs Widget
  smtk::simulation::ace3p::qtJobsWidget* jobsWidget = jobsBehavior->jobsWidget();
  QObject::connect(
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::selectStageClicked,
    jobsWidget,
    &smtk::simulation::ace3p::qtJobsWidget::onStageSelected);
  QObject::connect(
    newStageBehavior,
    &pqACE3PNewStageBehavior::stageAdded,
    jobsWidget,
    &smtk::simulation::ace3p::qtJobsWidget::onStageSelected);

  // pass ui button signals from qtStagesWidget up the chain
  QObject::connect(
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::addStageClicked,
    newStageBehavior,
    &pqACE3PNewStageBehavior::createNewStage);
  QObject::connect(
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::deleteStageClicked,
    this,
    &pqACE3PStagesPanel::deleteStage);
  QObject::connect(
    m_stageWidget,
    &smtk::simulation::ace3p::qtStagesWidget::selectStageClicked,
    stagesBehavior,
    &pqACE3PStagesBehavior::selectStage);

  QObject::connect(
    loader, &pqACE3PProjectLoader::projectOpened, this, &pqACE3PStagesPanel::triggerSelectStage);

  // hide the panel upon startup (only really required once - upon very first program load)
  QTimer::singleShot(50, stagesBehavior, &pqACE3PStagesBehavior::hideStagesPanel);
}

pqACE3PStagesPanel::~pqACE3PStagesPanel()
{
  auto pqCore = pqApplicationCore::instance();
  pqCore->unRegisterManager(QString("stage_panel"));
}

// NOTE - this is only currently used upon project load, if used for other use cases in the
//        future, the restoration of project dirty/clean status may need rethinking
void pqACE3PStagesPanel::triggerSelectStage(smtk::project::ProjectPtr project)
{
  auto ace3pProject = std::dynamic_pointer_cast<smtk::simulation::ace3p::Project>(project);
  if (ace3pProject->numberOfStages() > 0)
  {
    int index = static_cast<int>(ace3pProject->currentStageIndex());

    // bool bClean = ace3pProject->clean();  // SEE NOTE - may be problematic for future use cases

    // NOTE - for some reason, when index==0 the following DOES NOT WORK on Windows, fix this
    pqACE3PStagesBehavior::instance()->initStage(index);
    smtk::simulation::ace3p::qtJobsWidget* jobsWidget =
      pqACE3PJobsBehavior::instance()->jobsWidget();
    jobsWidget->onStageSelected(index);

    // ace3pProject->setClean(bClean);  // SEE NOTE - may be problematic for future use cases

    // set the visible highlighting of currently selected row
    m_stageWidget->setGUIRowSelection(index);
  }
}

void pqACE3PStagesPanel::infoSlot(const QString& msg)
{
  // Would like to emit signal to main window status bar
  // but that is not currently working. Instead, qInfo()
  // messages are sent to the CMB Log Window:
  qInfo() << "pqACE3PStagesPanel:" << msg;
}

void pqACE3PStagesPanel::deleteStage(int index)
{
  // Get current project
  auto* runtime = smtk::simulation::ace3p::qtProjectRuntime::instance();
  std::shared_ptr<smtk::simulation::ace3p::Project> ace3pProject = runtime->ace3pProject();
  if (!ace3pProject)
  {
    qWarning() << "Internal error - no active ace3p project.";
    return;
  }

  if (ace3pProject->numberOfStages() < 2)
  {
    QMessageBox::warning(
      this, "Remove Warning", "You cannot remove the last stage from a project.");
    return;
  }

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

  // Instantiate the Export operator
  auto removeStageOp = opManager->create<smtk::simulation::ace3p::RemoveStage>();
  InternalCheckMacro(removeStageOp != nullptr, "Internal Error: Export operation not created.");

  removeStageOp->parameters()->associate(ace3pProject);
  removeStageOp->parameters()->findInt("stage-index")->setValue(index);

  auto result = removeStageOp->operate();

  int outcome = result->findInt("outcome")->value();
  if (outcome != OP_SUCCEEDED)
  {
    QMessageBox::critical(
      this, "Remove Warning", QString("Stage deletion failed: outcome %1.").arg(outcome));
    return;
  }

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

  int currentStage = static_cast<int>(ace3pProject->currentStageIndex());

  emit updateCurrentStage(currentStage);
}

void pqACE3PStagesPanel::sourceRemoved(pqSMTKWrapper* mgr, pqServer* server)
{
  if (!mgr || !server)
  {
    return;
  }
}
