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

// ACE3P
#include "plugin/pqACE3PAutoStart.h"
#include "plugin/pqACE3PStagesPanel.h"
#include "smtk/simulation/ace3p/qt/qtProjectRuntime.h"

// SMTK
#include "smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKBehavior.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKResource.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
#include "smtk/model/Resource.h"
#include "smtk/resource/Resource.h"

// Client side
#include "pqApplicationCore.h"
#include "pqCoreUtilities.h"
#include "pqObjectBuilder.h"
#include "pqPipelineRepresentation.h"
#include "pqPipelineSource.h"
#include "pqRenderView.h"
#include "pqRepresentation.h"
#include "pqServer.h"
#include "pqServerManagerModel.h"

#include <QDebug>
#include <QList>
#include <QSet>

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

//-----------------------------------------------------------------------------
void pqACE3PStagesReaction::onTriggered()
{
  // Not currently used
}

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

//-----------------------------------------------------------------------------
pqACE3PStagesBehavior::pqACE3PStagesBehavior(QObject* parent)
  : Superclass(parent)
  , m_stagesPanel(nullptr)
{
}

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

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

  return g_instance;
}

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

  QObject::disconnect(this);
}

void pqACE3PStagesBehavior::hideStagesPanel()
{
  this->setPanelVisible(false);
}

void pqACE3PStagesBehavior::setPanelVisible(bool bVisible)
{
  m_stagesPanel->setVisible(bVisible);
}

void pqACE3PStagesBehavior::updateStagesPanelVisibility(
  std::shared_ptr<smtk::simulation::ace3p::Project> ace3pProject)
{
  bool bStagePanelVisible = ace3pProject->numberOfStages() > 1;
  this->setPanelVisible(bStagePanelVisible);
}

/*
bool pqACE3PStagesBehavior::setPanelVisibility(QString panelName, bool bVisible)
{
  QMainWindow* mainWindow = qobject_cast<QMainWindow*>(pqCoreUtilities::mainWidget());
  if (!mainWindow)
  {
    qDebug() << "pqACE3PStagesBehavior::setPanelVisibility - mainWindow is null";
    return false;
  }

  QList<QDockWidget*> all_docks = mainWindow->findChildren<QDockWidget*>();
  foreach (QDockWidget* dw, all_docks)
  {
    if (dw->objectName() == panelName)
    {
      qDebug() << "pqACE3PStagesBehavior::setPanelVisibility - found" << panelName;
      dw->setVisible(bVisible);
      return true;
    }
  }
  qDebug() << "pqACE3PStagesBehavior::setPanelVisibility - did not find" << panelName;
  return false;
}
*/

//-----------------------------------------------------------------------------
void pqACE3PStagesBehavior::initStage(int index)
{
  // Make sure that smtk UI elements are initialized
  pqCoreUtilities::processEvents();

  // Traverse pqSMTKResource instances to build m_modelSourceMap;
  m_modelResourceMap.clear();
  pqServer* builtinServer = pqACE3PAutoStart::builtinServer();
  pqSMTKWrapper* wrapper = pqSMTKBehavior::instance()->resourceManagerForServer(builtinServer);
  wrapper->visitResources([&](pqSMTKResource* pipelineResource) {
    smtk::resource::ResourcePtr resource = pipelineResource->getResource();
    if (resource && resource->isOfType<smtk::model::Resource>())
    {
      m_modelResourceMap[resource] = pipelineResource;
    }
    return false;
  });

  this->setStage(index, false);
}

//-----------------------------------------------------------------------------
void pqACE3PStagesBehavior::selectStage(int index)
{
  this->setStage(index, true);
}

//-----------------------------------------------------------------------------
void pqACE3PStagesBehavior::setStage(int index, bool updateProject)
{
  // 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." << __FILE__ << __LINE__;
    return;
  }

  if (index < 0 || index >= ace3pProject->numberOfStages())
  {
    qWarning() << "Internal error - invalid stage index." << __FILE__ << __LINE__;
    return;
  }

  ///////////////////////////////////////////////////////////////////////////////
  /// Trigger update of Attribute Panel
  auto attrResource = ace3pProject->stage(index)->attributeResource();
  if (attrResource == nullptr)
  {
    qWarning() << "Internal error - stage" << index << "missing attribiute resource" << __FILE__
               << __LINE__;
    return;
  }

  // Find the attribute panel and set it to display the stage's resource.
  QWidget* mainWidget = pqCoreUtilities::mainWidget();
  if (!mainWidget)
  {
    qWarning() << "Internal error - no main widget." << __FILE__ << __LINE__;
    return;
  }

  foreach (pqSMTKAttributePanel* attrPanel, mainWidget->findChildren<pqSMTKAttributePanel*>())
  {
    attrPanel->displayResourceOnServer(attrResource);
    break; // should only be ONE attribute panel
  }

  this->setModelVisiblity(ace3pProject, index);

  ///////////////////////////////////////////////////////////////////////////////
  /// Update the project
  if (updateProject)
  {
    ace3pProject->setCurrentStage(index, true); // marks the project as dirty
  }

  emit this->stageSelected(index); // currently this signal doesn't seem to be used for anything
}

//-----------------------------------------------------------------------------
void pqACE3PStagesBehavior::setModelVisiblity(
  std::shared_ptr<smtk::simulation::ace3p::Project> ace3pProject,
  int index)
{
  // Get model resource for this stage
  auto currentModelRes = ace3pProject->stage(index)->modelResource();
  if (currentModelRes == nullptr)
  {
    qWarning() << "Stage" << index << "has no model resource. Not updating renderviews";
    return;
  }

  // Get pipeline source for this model resource
  pqPipelineSource* currentSource = m_modelResourceMap.value(currentModelRes, nullptr);
  if (currentSource == nullptr)
  {
    qWarning() << "Stage" << index << "has no pipeline source for model resource"
               << currentModelRes->name().c_str() << " -- Not updating renderviews";
    return;
  }

  // Create set of pipeline sources for other models in the project
  QSet<pqPipelineSource*> otherSources;
  // Initialize explicitly in order to remain compatible with Qt 5.12
  foreach (pqPipelineSource* source, m_modelResourceMap.values())
  {
    otherSources += source;
  }
  otherSources.remove(currentSource);

  // Update all render views connected to builtin server
  pqServer* builtinServer = pqACE3PAutoStart::builtinServer();
  pqServerManagerModel* smModel = pqApplicationCore::instance()->getServerManagerModel();
  QList<pqRenderView*> viewList = smModel->findItems<pqRenderView*>(builtinServer);
  foreach (pqRenderView* view, viewList)
  {
    bool modelsVisible = false;
    pqRepresentation* currentModelRep = nullptr;

    // Traverse representations
    QList<pqRepresentation*> repList = view->getRepresentations();
    for (auto rep : repList)
    {
      auto* pipelineRep = dynamic_cast<pqPipelineRepresentation*>(rep);
      if (pipelineRep == nullptr)
      {
        continue;
      }

      pqPipelineSource* source = pipelineRep->getInput();
      if (source == nullptr)
      {
        continue;
      }

      if (source == currentSource)
      {
        currentModelRep = rep;
        continue;
      }

      if (otherSources.contains(source) && rep->isVisible())
      {
        modelsVisible = true;
        rep->setVisible(false);
      }
    }

    if (currentModelRep == nullptr)
    {
      continue;
    }

    // If models are visible in this view or if the view is empty,
    // then make the current stage's model visible.
    // qDebug() << __LINE__ << modelsVisible << view->getNumberOfVisibleRepresentations() << currentModelRep;
    bool showModel = modelsVisible || (view->getNumberOfVisibleRepresentations() < 1);
    if (showModel)
    {
      currentModelRep->setVisible(true);
      view->render();
    }
  }
}
