//=========================================================================
//  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 "smtk/session/rgg/plugin/smtkRGGSelectionBehavior.h"

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

#include "smtk/PublicPointerDefs.h"
#include "smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h"
#include "smtk/extension/paraview/server/vtkSMSMTKWrapperProxy.h"

#include "smtk/model/Entity.h"
#include "smtk/model/EntityIterator.h"
#include "smtk/model/EntityRef.h"
#include "smtk/model/Resource.h"

#include "smtk/resource/Component.h"

#include "smtk/view/Selection.h"

#include "smtk/resource/Resource.h"

#include "smtk/io/Logger.h"

// Client side
#include "pqActiveObjects.h"
#include "pqLiveInsituManager.h"
#include "pqPipelineSource.h"
#include "pqSelectionManager.h"
#include "pqServerManagerModel.h"

// Qt
#include <QTimer>
#include <QWidget>

using namespace smtk;

static smtkRGGSelectionBehavior* g_rggSelection = nullptr;

int UpdateVisibilityForFootprint(pqSMTKResourceRepresentation* smap, const smtk::resource::ComponentPtr& comp, int visible)
{
  bool didUpdate = false;
  int rval(0);

  if (auto ment = std::dynamic_pointer_cast<smtk::model::Entity>(comp))
  {
    if (ment->isModel() || ment->isGroup())
    {
      int any = 0;
      smtk::model::EntityIterator childIt;
      smtk::model::EntityRef entRef = ment->template referenceAs<smtk::model::EntityRef>();
      childIt.traverse(entRef, smtk::model::IteratorStyle::ITERATE_CHILDREN);
      for (childIt.begin(); !childIt.isAtEnd(); ++childIt)
      {
        auto child = childIt.current().entityRecord();
        int ok = smap->setVisibility(child, visible);
        any |= ok;
      }
      rval = any;
      if (any)
      {
        didUpdate = true;
      }
    }
    else
    {
      // Composite auxliliary geometry condition
      int any = 0;
      smtk::model::AuxiliaryGeometry auxgeom =
        ment->template referenceAs<smtk::model::AuxiliaryGeometry>();
      auto auxgeomChildren = auxgeom.auxiliaryGeometries();
      if (auxgeom && !auxgeomChildren.empty())
      {
        for (const auto& child : auxgeomChildren)
        {
          int ok = smap->setVisibility(child.component(), visible ? true : false);
          any |= ok;
        }
      }
      rval |= any;

      rval |= smap->setVisibility(comp, visible ? true : false) ? 1 : 0;
      if (rval)
      {
        didUpdate = true;
      }
    }
  }

  return rval;
}

smtkRGGSelectionBehavior::smtkRGGSelectionBehavior(QObject* parent)
  : Superclass(parent)
  , m_changingSource(false)
  , m_selectionValue("rgg selected")
{
  if (!g_rggSelection)
  {
    g_rggSelection = this;
  }

  auto& activeObjects = pqActiveObjects::instance();

  QObject::connect(&activeObjects, SIGNAL(sourceChanged(pqPipelineSource*)), this,
    SLOT(onActiveSourceChanged(pqPipelineSource*)));

  // Track server connects/disconnects
  auto rsrcBehavior = pqSMTKBehavior::instance();
  // listen to current server.
  rsrcBehavior->visitResourceManagersOnServers([this](pqSMTKWrapper* r, pqServer* s) {
    this->observeSelectionOnServer(r, s);
    return false;
  });
  // track future server connect/disconnect.
  QObject::connect(rsrcBehavior, SIGNAL(addedManagerOnServer(pqSMTKWrapper*, pqServer*)),
    this, SLOT(observeSelectionOnServer(pqSMTKWrapper*, pqServer*)));
  QObject::connect(rsrcBehavior,
    SIGNAL(removingManagerFromServer(pqSMTKWrapper*, pqServer*)), this,
    SLOT(unobserveSelectionOnServer(pqSMTKWrapper*, pqServer*)));
}

smtkRGGSelectionBehavior::~smtkRGGSelectionBehavior()
{
  if (g_rggSelection == this)
  {
    g_rggSelection = nullptr;
  }
}

smtkRGGSelectionBehavior* smtkRGGSelectionBehavior::instance(QObject* parent)
{
  if (!g_rggSelection)
  {
    g_rggSelection = new smtkRGGSelectionBehavior(parent);
  }

  return g_rggSelection;
}

void smtkRGGSelectionBehavior::setSelectionValue(const std::string& selectionValue)
{
  if (m_selectionValue != selectionValue)
  {
    m_selectionValue = selectionValue;
  }
}

void smtkRGGSelectionBehavior::observeSelectionOnServer(
  pqSMTKWrapper* mgr, pqServer* server)
{
  (void)server;
  if (!mgr || !mgr->smtkProxy())
  {
    std::cerr << "smtkRGGSelectionBehavior::observeSelectionOnServer: no wrapper\n";
    return;
  }
  auto seln = mgr->smtkProxy()->GetSelection();
  if (!seln)
  {
    std::cerr << "smtkRGGSelectionBehavior::observeSelectionOnServer: no selection\n";
    return;
  }

  auto observerId = seln->observers().insert(
    [&](const std::string& source, smtk::view::SelectionPtr selection) {
      smtk::view::Selection::SelectionMap selectionMap = selection->currentSelection();
      smtk::resource::ResourcePtr rsrc;
      smtk::resource::ComponentPtr selectedComp;
      // Find the resource pointer
      for (auto& sel : selectionMap)
      {
        smtk::resource::ComponentPtr comp =  std::dynamic_pointer_cast<smtk::resource::Component>(sel.first);
        if (comp)
        {
          selectedComp = comp;
          rsrc = comp->resource();
          break;
        }
      }
      if (!rsrc){
        return;
      }
      // Get the current pqSMTKResourceRepresentation
      auto smtkBehavior =  pqSMTKBehavior::instance();
      pqSMTKResource* pvResource = smtkBehavior->getPVResource(rsrc);

      pqView* view = pqActiveObjects::instance().activeView();
      pqDataRepresentation* pqDataRep = pvResource ? pvResource->getRepresentation(view) : nullptr;
      pqSMTKResourceRepresentation* pqSMTKResRep = dynamic_cast<pqSMTKResourceRepresentation*>(pqDataRep);
      // Hide every thing first
      smtk::resource::ComponentSet modelCompSet = rsrc->filter("model|3");
      if (modelCompSet.size()){
        UpdateVisibilityForFootprint(pqSMTKResRep, *modelCompSet.begin(), false);
      }
      // Make the selected components visible
      for (auto& sel : selectionMap)
      {
        smtk::resource::ComponentPtr comp =  std::dynamic_pointer_cast<smtk::resource::Component>(sel.first);
        if (comp)
        {
          UpdateVisibilityForFootprint(pqSMTKResRep, comp, true);
        }
      }

      // Clear the selection so that entities will not be rendered with the default selection color.
      // TODO: clearing the selection prevents choosing relevant operations. Need a way to disable the selection color.
      // QTimer::singleShot(1, [selection](){
      //   selection->modifySelection(std::vector<smtk::resource::PersistentObjectPtr>{}, "smtkRGGSelectionBehavior", 1
      //                              , smtk::view::SelectionAction::UNFILTERED_REPLACE);
      // });
    },
    "smtkRGGSelectionBehavior: RGG session oriented selection.");
  m_selectionObservers[seln] = std::move(observerId);
}

void smtkRGGSelectionBehavior::unobserveSelectionOnServer(
  pqSMTKWrapper* mgr, pqServer* server)
{
  (void)server;
  if (!mgr || !mgr->smtkProxy())
  {
    return;
  }
  auto seln = mgr->smtkProxy()->GetSelection();
  if (!seln)
  {
    return;
  }

  auto entry = m_selectionObservers.find(seln);
  if (entry != m_selectionObservers.end())
  {
    seln->observers().erase(entry->second);
    m_selectionObservers.erase(entry);
  }
}

void smtkRGGSelectionBehavior::onActiveSourceChanged(pqPipelineSource* source)
{
  auto rsrc = dynamic_cast<pqSMTKResource*>(source);
  if (rsrc && !m_changingSource)
  {
    // We need to update the SMTK selection to be the active pipeline source.
    smtk::resource::PersistentObjectSet activeResources;
    activeResources.insert(rsrc->getResource());

    m_changingSource = true;
    auto behavior = pqSMTKBehavior::instance();
    behavior->visitResourceManagersOnServers(
      [this, &activeResources](pqSMTKWrapper* wrapper, pqServer* server) -> bool {
        // skip bad servers
        if (!wrapper || pqLiveInsituManager::isInsituServer(server))
        {
          return false;
        }

        auto seln = wrapper->smtkSelection();
        if (!seln)
        {
          return false;
        }

        int value = seln->selectionValueFromLabel(m_selectionValue);
        seln->modifySelection(activeResources, "smtkRGGSelectionBehavior", value,
          smtk::view::SelectionAction::UNFILTERED_REPLACE, true);

        return false;
      });
    m_changingSource = false;
  }
}
