/*=========================================================================

  Program:   ParaView
  Module:    vtkSMParaViewPipelineController.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkSMParaViewPipelineController.h"

#include "vtkCommand.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPVProxyDefinitionIterator.h"
#include "vtkPVXMLElement.h"
#include "vtkSmartPointer.h"
#include "vtkSMGlobalPropertiesProxy.h"
#include "vtkSMProperty.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMPropertyIterator.h"
#include "vtkSMPropertyLink.h"
#include "vtkSMProxyDefinitionManager.h"
#include "vtkSMProxyIterator.h"
#include "vtkSMProxyListDomain.h"
#include "vtkSMProxyProperty.h"
#include "vtkSMProxySelectionModel.h"
#include "vtkSMSession.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSettings.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMTimeKeeperProxy.h"
#include "vtkSMTrace.h"
#include "vtkWeakPointer.h"

#include <cassert>
#include <map>
#include <set>
#include <vector>
#include <sstream>

class vtkSMParaViewPipelineController::vtkInternals
{
public:
  typedef std::map<void*, vtkTimeStamp> TimeStampsMap;
  TimeStampsMap InitializationTimeStamps;
  std::set<void*> ProxiesBeingUnRegistered;
};


namespace
{
  // Used to monitor properties whose domains change.
  class vtkDomainObserver
    {
    std::vector<std::pair<vtkSMProperty*, unsigned long> > MonitoredProperties;
    std::set<vtkSMProperty*> PropertiesWithModifiedDomains;

    void DomainModified(vtkObject* sender, unsigned long, void*)
      {
      vtkSMProperty* prop = vtkSMProperty::SafeDownCast(sender);
      if (prop)
        {
        this->PropertiesWithModifiedDomains.insert(prop);
        }
      }

  public:
    vtkDomainObserver()
      {
      }
    ~vtkDomainObserver()
      {
      for (size_t cc=0; cc < this->MonitoredProperties.size(); cc++)
        {
        this->MonitoredProperties[cc].first->RemoveObserver(
          this->MonitoredProperties[cc].second);
        }
      }
    void Monitor(vtkSMProperty* prop)
      {
      assert(prop != NULL);
      unsigned long oid = prop->AddObserver(vtkCommand::DomainModifiedEvent,
        this, &vtkDomainObserver::DomainModified);
      this->MonitoredProperties.push_back(
        std::pair<vtkSMProperty*, unsigned long>(prop, oid));
      }

    const std::set<vtkSMProperty*>& GetPropertiesWithModifiedDomains() const
      {
      return this->PropertiesWithModifiedDomains;
      }
    };

  // method to create a new proxy safely i.e. not produce warnings if definition
  // is not available.
  inline vtkSMProxy* vtkSafeNewProxy(vtkSMSessionProxyManager* pxm,
    const char* group, const char* name)
    {
    if (pxm && pxm->GetPrototypeProxy(group, name))
      {
      return pxm->NewProxy(group, name);
      }
    return NULL;
    }

  class vtkPrepareForUnregisteringScopedObj
  {
    void* Proxy;
    std::set<void*> &ProxiesBeingUnRegistered;
  public:
    vtkPrepareForUnregisteringScopedObj(void* proxy, std::set<void*>& theset) :
      Proxy(proxy),
      ProxiesBeingUnRegistered(theset)
      {
      this->ProxiesBeingUnRegistered.insert(this->Proxy);
      }
    ~vtkPrepareForUnregisteringScopedObj()
      {
      this->ProxiesBeingUnRegistered.erase(this->Proxy);
      }
  private:
    vtkPrepareForUnregisteringScopedObj(const vtkPrepareForUnregisteringScopedObj&);
    void operator=(const vtkPrepareForUnregisteringScopedObj&);
  };
}

//---------------------------------------------------------------------------------------------------------
// This macros makes it easier to avoid running into unregistering loops due to dependency cycles
// when unregistering proxies. Any concrete method that unregisters proxies should simply call this macro.
#define PREPARE_FOR_UNREGISTERING(arg)\
  if (!arg) { return false; } \
if (this->Internals->ProxiesBeingUnRegistered.find(arg) != this->Internals->ProxiesBeingUnRegistered.end()) \
{ \
  return true; \
} \
vtkPrepareForUnregisteringScopedObj __tmp(arg, this->Internals->ProxiesBeingUnRegistered);

vtkObjectFactoryNewMacro(vtkSMParaViewPipelineController);
//----------------------------------------------------------------------------
vtkSMParaViewPipelineController::vtkSMParaViewPipelineController()
  : Internals(new vtkSMParaViewPipelineController::vtkInternals())
{
}

//----------------------------------------------------------------------------
vtkSMParaViewPipelineController::~vtkSMParaViewPipelineController()
{
  delete this->Internals;
  this->Internals = NULL;
}

//----------------------------------------------------------------------------
vtkStdString vtkSMParaViewPipelineController::GetHelperProxyGroupName(vtkSMProxy* proxy)
{
  assert(proxy != NULL);
  std::ostringstream groupnamestr;
  groupnamestr << "pq_helper_proxies." << proxy->GetGlobalIDAsString();
  return groupnamestr.str();
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::FindProxy(
  vtkSMSessionProxyManager* pxm, const char* reggroup, const char* xmlgroup, const char* xmltype)
{
  return pxm? pxm->FindProxy(reggroup, xmlgroup, xmltype) : NULL;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::CreateProxiesForProxyListDomains(
  vtkSMProxy* proxy)
{
  assert(proxy != NULL);
  vtkSmartPointer<vtkSMPropertyIterator> iter;
  iter.TakeReference(proxy->NewPropertyIterator());
  for (iter->Begin(); !iter->IsAtEnd(); iter->Next())
    {
    vtkSMProxyListDomain* pld = iter->GetProperty()?
      vtkSMProxyListDomain::SafeDownCast(iter->GetProperty()->FindDomain("vtkSMProxyListDomain"))
      : NULL;
    if (pld)
      {
      pld->CreateProxies(proxy->GetSessionProxyManager());
      for (unsigned int cc=0, max=pld->GetNumberOfProxies(); cc < max; cc++)
        {
        this->PreInitializeProxy(pld->GetProxy(cc));
        }
      // this is unnecessary here. only done for CompoundSourceProxy instances.
      // those proxies, we generally skip calling "reset" on in
      // PostInitializeProxy(). However, for properties that have proxy
      // list domains, we need to reset them (e.g ProbeLine filter).
      iter->GetProperty()->ResetToDomainDefaults();
      }
    }
  return true;
}

//----------------------------------------------------------------------------
void vtkSMParaViewPipelineController::RegisterProxiesForProxyListDomains(
  vtkSMProxy* proxy)
{
  assert(proxy != NULL);
  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();

  std::string groupname = this->GetHelperProxyGroupName(proxy);

  vtkSmartPointer<vtkSMPropertyIterator> iter;
  iter.TakeReference(proxy->NewPropertyIterator());
  for (iter->Begin(); !iter->IsAtEnd(); iter->Next())
    {
    vtkSMProxyListDomain* pld = iter->GetProperty()?
      vtkSMProxyListDomain::SafeDownCast(iter->GetProperty()->FindDomain("vtkSMProxyListDomain"))
      : NULL;
    if (!pld)
      {
      continue;
      }
    for (unsigned int cc=0, max=pld->GetNumberOfProxies(); cc < max; cc++)
      {
      vtkSMProxy* plproxy = pld->GetProxy(cc);

      // Handle proxy-list hints.
      this->ProcessProxyListProxyHints(proxy, plproxy);

      this->PostInitializeProxy(plproxy);
      pxm->RegisterProxy(groupname.c_str(), iter->GetKey(), plproxy);
      }
    }
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::SetupGlobalPropertiesLinks(vtkSMProxy* proxy)
{
  assert(proxy != NULL);

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();

  vtkSmartPointer<vtkSMPropertyIterator> iter;
  iter.TakeReference(proxy->NewPropertyIterator());
  for (iter->Begin(); !iter->IsAtEnd(); iter->Next())
    {
    vtkSMProperty* prop = iter->GetProperty();
    assert(prop);

    vtkPVXMLElement* linkHint = prop->GetHints()?
      prop->GetHints()->FindNestedElementByName("GlobalPropertyLink") : NULL;
    if (linkHint)
      {
      std::string type = linkHint->GetAttributeOrEmpty("type");
      std::string property = linkHint->GetAttributeOrEmpty("property");
      if (type.empty() || property.empty())
        {
        vtkWarningMacro("Invalid GlobalPropertyLink hint.");
        continue;
        }

      vtkSMGlobalPropertiesProxy* gpproxy = vtkSMGlobalPropertiesProxy::SafeDownCast(
        pxm->GetProxy("global_properties", type.c_str()));
      if (!gpproxy)
        {
        continue;
        }
      if (!gpproxy->Link(property.c_str(), proxy, iter->GetKey()))
        {
        vtkWarningMacro("Failed to setup GlobalPropertyLink.");
        }
      }

    // Check for PropertyLinks
    linkHint = prop->GetHints()?
      prop->GetHints()->FindNestedElementByName("PropertyLink") : NULL;
    if (linkHint)
      {
      const char* sourceGroupName = linkHint->GetAttributeOrEmpty("group");
      const char* sourceProxyName = linkHint->GetAttributeOrEmpty("proxy");
      const char* sourcePropertyName = linkHint->GetAttributeOrEmpty("property");
      if (!sourceGroupName || !sourceProxyName || !sourcePropertyName)
        {
        continue;
        }

      vtkSMProxy* sourceProxy = pxm->GetProxy(sourceGroupName, sourceProxyName);
      if (!sourceProxy)
        {
        continue;
        }

      vtkSMProperty* sourceProperty =  sourceProxy->GetProperty(sourcePropertyName);
      if (sourceProperty)
        {
        sourceProperty->AddLinkedProperty(prop);
        }
      else
        {
        vtkWarningMacro(<< "No source property with group \"" << sourceGroupName
                        << "\", proxy \"" << sourceProxyName
                        << "\", property \"" << sourcePropertyName
                        << "\" exists. Linking not performed.")
        }
      }
    }
  return true;
}

namespace vtkSMParaViewPipelineControllerNS
{
  //---------------------------------------------------------------------------
  class vtkSMLinkObserver : public vtkCommand
  {
public:
  vtkWeakPointer<vtkSMProperty> Output;
  typedef vtkCommand Superclass;
  virtual const char* GetClassNameInternal() const
    { return "vtkSMLinkObserver"; }
  static vtkSMLinkObserver* New()
    {
    return new vtkSMLinkObserver();
    }
  virtual void Execute(vtkObject* caller, unsigned long event, void* calldata)
    {
    (void)event;
    (void)calldata;
    vtkSMProperty* input = vtkSMProperty::SafeDownCast(caller);
    if (input && this->Output)
      {
      // this will copy both checked and unchecked property values.
      this->Output->Copy(input);
      }
    }

  static void LinkProperty(vtkSMProperty* input,
    vtkSMProperty* output)
    {
    if (input && output)
      {
      vtkSMLinkObserver* observer = vtkSMLinkObserver::New();
      observer->Output = output;
      input->AddObserver(vtkCommand::PropertyModifiedEvent, observer);
      input->AddObserver(vtkCommand::UncheckedPropertyModifiedEvent, observer);
      observer->FastDelete();
      output->Copy(input);
      }
    }
  };

}

//----------------------------------------------------------------------------
void vtkSMParaViewPipelineController::ProcessProxyListProxyHints(
  vtkSMProxy* parent, vtkSMProxy* plproxy)
{
  vtkPVXMLElement* proxyListElement = plproxy->GetHints()?
    plproxy->GetHints()->FindNestedElementByName("ProxyList") : NULL;
  if (!proxyListElement)
    {
    return;
    }
  for (unsigned int cc=0, max = proxyListElement->GetNumberOfNestedElements();
    cc < max; ++cc)
    {
    vtkPVXMLElement* child = proxyListElement->GetNestedElement(cc);
    if (child && child->GetName() && strcmp(child->GetName(), "Link") == 0)
      {
      const char* name = child->GetAttribute("name");
      const char* linked_with = child->GetAttribute("with_property");
      if (name && linked_with)
        {
        vtkSMParaViewPipelineControllerNS::vtkSMLinkObserver::LinkProperty(
          parent->GetProperty(linked_with), plproxy->GetProperty(name));
        }
      }
    }
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::CreateAnimationHelpers(vtkSMProxy* proxy)
{
  vtkSMSourceProxy* source = vtkSMSourceProxy::SafeDownCast(proxy);
  if (!source)
    {
    return false;
    }
  assert(proxy != NULL);
  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();

  std::string groupname = this->GetHelperProxyGroupName(proxy);

  for (unsigned int cc=0, max=source->GetNumberOfOutputPorts(); cc < max; cc++)
    {
    vtkSmartPointer<vtkSMProxy> helper;
    helper.TakeReference(vtkSafeNewProxy(pxm, "misc", "RepresentationAnimationHelper"));
    if (helper) // since this is optional
      {
      this->PreInitializeProxy(helper);
      vtkSMPropertyHelper(helper, "Source").Set(proxy);
      this->PostInitializeProxy(helper);
      helper->UpdateVTKObjects();

      // yup, all are registered under same name.
      pxm->RegisterProxy(
        groupname.c_str(), "RepresentationAnimationHelper", helper);
      }
    }

  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::InitializeSession(vtkSMSession* session)
{
  assert(session != NULL);

  vtkSMSessionProxyManager* pxm = session->GetSessionProxyManager();
  assert(pxm);

  //---------------------------------------------------------------------------
  // If the session is a collaborative session, we need to fetch the state from
  // server before we start creating "essential" proxies. This is a no-op if not
  // a collaborative session.
  pxm->UpdateFromRemote();

  //---------------------------------------------------------------------------
  // Setup selection models used to track active view/active proxy.
  vtkSMProxySelectionModel* selmodel = pxm->GetSelectionModel("ActiveSources");
  if (selmodel == NULL)
    {
    selmodel = vtkSMProxySelectionModel::New();
    pxm->RegisterSelectionModel("ActiveSources", selmodel);
    selmodel->FastDelete();
    }

  selmodel = pxm->GetSelectionModel("ActiveView");
  if (selmodel == NULL)
    {
    selmodel = vtkSMProxySelectionModel::New();
    pxm->RegisterSelectionModel("ActiveView", selmodel);
    selmodel->FastDelete();
    }

  //---------------------------------------------------------------------------
  // Create the timekeeper if none exists.
  vtkSmartPointer<vtkSMProxy> timeKeeper = this->FindTimeKeeper(session);
  if (!timeKeeper)
    {
    timeKeeper.TakeReference(vtkSafeNewProxy(pxm, "misc", "TimeKeeper"));
    if (!timeKeeper)
      {
      vtkErrorMacro("Failed to create 'TimeKeeper' proxy. ");
      return false;
      }
    this->InitializeProxy(timeKeeper);
    timeKeeper->UpdateVTKObjects();

    pxm->RegisterProxy("timekeeper", timeKeeper);
    }

  //---------------------------------------------------------------------------
  // Create the animation-scene (optional)
  vtkSMProxy* animationScene = this->GetAnimationScene(session);
  if (animationScene)
    {
    // create time-animation track (optional)
    this->GetTimeAnimationTrack(animationScene);
    }

  //---------------------------------------------------------------------------
  // Setup global settings/state for the visualization state.
  this->UpdateSettingsProxies(session);

  //---------------------------------------------------------------------------
  // Setup color palette and proxies for other global property groups (optional)
  vtkSMProxy* proxy = pxm->GetProxy("global_properties", "ColorPalette");
  if (!proxy)
    {
    proxy = vtkSafeNewProxy(pxm, "misc", "ColorPalette");
    if (proxy)
      {
      this->InitializeProxy(proxy);
      pxm->RegisterProxy("global_properties", "ColorPalette", proxy);
      proxy->UpdateVTKObjects();
      proxy->Delete();
      }
    }
  return true;
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::FindTimeKeeper(vtkSMSession* session)
{
  assert(session != NULL);

  vtkSMSessionProxyManager* pxm = session->GetSessionProxyManager();
  assert(pxm);

  return this->FindProxy(pxm, "timekeeper", "misc", "TimeKeeper");
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::FindAnimationScene(vtkSMSession* session)
{
  assert(session != NULL);

  vtkSMSessionProxyManager* pxm = session->GetSessionProxyManager();
  assert(pxm);

  return this->FindProxy(pxm, "animation", "animation", "AnimationScene");
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::GetAnimationScene(vtkSMSession* session)
{
  assert(session != NULL);

  vtkSMSessionProxyManager* pxm = session->GetSessionProxyManager();
  assert(pxm);

  vtkSMProxy* timeKeeper = this->FindTimeKeeper(session);
  if (!timeKeeper)
    {
    return NULL;
    }

  vtkSmartPointer<vtkSMProxy> animationScene = this->FindAnimationScene(session);
  if (!animationScene)
    {
    animationScene.TakeReference(vtkSafeNewProxy(pxm, "animation", "AnimationScene"));
    if (animationScene)
      {
      this->PreInitializeProxy(animationScene);
      vtkSMPropertyHelper(animationScene, "TimeKeeper").Set(timeKeeper);
      this->PostInitializeProxy(animationScene);
      animationScene->UpdateVTKObjects();
      pxm->RegisterProxy("animation", animationScene);
      }
    }
  return animationScene.GetPointer();
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::FindTimeAnimationTrack(vtkSMProxy* scene)
{
  if (!scene)
    {
    return NULL;
    }

  vtkSMProxy* timeKeeper = this->FindTimeKeeper(scene->GetSession());
  if (!timeKeeper)
    {
    return NULL;
    }

  vtkSMPropertyHelper helper(scene, "Cues", /*quiet*/ true);
  for (unsigned int cc=0, max=helper.GetNumberOfElements(); cc < max; cc++)
    {
    vtkSMProxy* cue = helper.GetAsProxy(cc);
    if (cue && cue->GetXMLName() &&
      strcmp(cue->GetXMLName(), "TimeAnimationCue") == 0 &&
      vtkSMPropertyHelper(cue, "AnimatedProxy").GetAsProxy() == timeKeeper)
      {
      vtkSMPropertyHelper pnameHelper(cue, "AnimatedPropertyName");
      if (pnameHelper.GetAsString(0) &&
        strcmp(pnameHelper.GetAsString(0), "Time") == 0)
        {
        return cue;
        }
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkSMProxy* vtkSMParaViewPipelineController::GetTimeAnimationTrack(vtkSMProxy* scene)
{
  vtkSmartPointer<vtkSMProxy> cue = this->FindTimeAnimationTrack(scene);
  if (cue || !scene)
    {
    return cue;
    }

  vtkSMProxy* timeKeeper = this->FindTimeKeeper(scene->GetSession());
  if (!timeKeeper)
    {
    return NULL;
    }

  vtkSMSessionProxyManager* pxm = scene->GetSessionProxyManager();
  assert(pxm);

  cue.TakeReference(vtkSafeNewProxy(pxm, "animation", "TimeAnimationCue"));
  if (!cue)
    {
    return NULL;
    }

  this->PreInitializeProxy(cue);
  vtkSMPropertyHelper(cue, "AnimatedProxy").Set(timeKeeper);
  vtkSMPropertyHelper(cue, "AnimatedPropertyName").Set("Time");
  this->PostInitializeProxy(cue);
  cue->UpdateVTKObjects();
  pxm->RegisterProxy("animation", cue);

  vtkSMPropertyHelper(scene, "Cues").Add(cue);
  scene->UpdateVTKObjects();

  return cue;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterPipelineProxy(
  vtkSMProxy* proxy, const char* proxyname)
{
  if (!proxy)
    {
    return false;
    }

  SM_SCOPED_TRACE(RegisterPipelineProxy).arg("proxy", proxy);

  // Create animation helpers for this proxy.
  this->CreateAnimationHelpers(proxy);

  // Now register the proxy itself.
  // If proxyname is NULL, the proxy manager makes up a name.
  proxy->GetSessionProxyManager()->RegisterProxy("sources", proxyname, proxy);

  // Register proxy with TimeKeeper.
  vtkSMProxy* timeKeeper = this->FindTimeKeeper(proxy->GetSession());
  vtkSMTimeKeeperProxy::AddTimeSource(timeKeeper, proxy,
    /*suppress_input*/ (
      proxy->GetProperty("TimestepValues") != NULL ||
      proxy->GetProperty("TimeRange") != NULL));

  // Make the proxy active.
  vtkSMProxySelectionModel* selmodel =
    proxy->GetSessionProxyManager()->GetSelectionModel("ActiveSources");
  assert(selmodel != NULL);
  selmodel->SetCurrentProxy(proxy, vtkSMProxySelectionModel::CLEAR_AND_SELECT);

  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterPipelineProxy(vtkSMProxy* proxy)
{
  PREPARE_FOR_UNREGISTERING(proxy);

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
  const char* _proxyname = pxm->GetProxyName("sources", proxy);
  if (_proxyname == NULL)
    {
    return false;
    }

  SM_SCOPED_TRACE(Delete).arg("proxy", proxy);

  const std::string proxyname(_proxyname);

  // ensure proxy is no longer active.
  vtkSMProxySelectionModel* selmodel = pxm->GetSelectionModel("ActiveSources");
  assert(selmodel != NULL);
  if (selmodel->GetCurrentProxy() == proxy)
    {
    selmodel->SetCurrentProxy(NULL, vtkSMProxySelectionModel::CLEAR_AND_SELECT);
    }

  // remove proxy from TimeKeeper.
  vtkSMProxy* timeKeeper = this->FindTimeKeeper(proxy->GetSession());
  vtkSMTimeKeeperProxy::RemoveTimeSource(timeKeeper, proxy,
    /*unsuppress_input*/ (
      proxy->GetProperty("TimestepValues") != NULL ||
      proxy->GetProperty("TimeRange") != NULL));

  // unregister dependencies.
  this->UnRegisterDependencies(proxy);

  // this will remove both proxy-list-domain helpers and animation helpers.
  this->FinalizeProxy(proxy);

  // unregister the proxy.
  pxm->UnRegisterProxy("sources", proxyname.c_str(), proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterViewProxy(
  vtkSMProxy* proxy, const char* proxyname)
{
  if (!proxy)
    {
    return false;
    }

  SM_SCOPED_TRACE(RegisterViewProxy).arg("proxy", proxy);

  // Now register the proxy itself.
  proxy->GetSessionProxyManager()->RegisterProxy("views", proxyname, proxy);

  // Register proxy with TimeKeeper.
  vtkSMProxy* timeKeeper = this->FindTimeKeeper(proxy->GetSession());
  vtkSMPropertyHelper(timeKeeper, "Views").Add(proxy);
  timeKeeper->UpdateVTKObjects();

  // Register proxy with AnimationScene (optional)
  vtkSMProxy* scene = this->GetAnimationScene(proxy->GetSession());
  if (scene)
    {
    vtkSMPropertyHelper(scene, "ViewModules").Add(proxy);
    scene->UpdateVTKObjects();
    }

  // Make the proxy active.
  vtkSMProxySelectionModel* selmodel =
    proxy->GetSessionProxyManager()->GetSelectionModel("ActiveView");
  assert(selmodel != NULL);
  selmodel->SetCurrentProxy(proxy, vtkSMProxySelectionModel::CLEAR_AND_SELECT);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterViewProxy(
  vtkSMProxy* proxy, bool unregister_representations/*=true*/)
{
  PREPARE_FOR_UNREGISTERING(proxy);

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
  const char* _proxyname = pxm->GetProxyName("views", proxy);
  if (_proxyname == NULL)
    {
    return false;
    }

  SM_SCOPED_TRACE(Delete).arg("proxy", proxy);
  const std::string proxyname(_proxyname);

  // ensure proxy is no longer active.
  vtkSMProxySelectionModel* selmodel = pxm->GetSelectionModel("ActiveView");
  assert(selmodel != NULL);
  if (selmodel->GetCurrentProxy() == proxy)
    {
    selmodel->SetCurrentProxy(NULL, vtkSMProxySelectionModel::CLEAR_AND_SELECT);
    }

  // remove proxy from AnimationScene (optional)
  vtkSMProxy* scene = this->GetAnimationScene(proxy->GetSession());
  if (scene)
    {
    vtkSMPropertyHelper(scene, "ViewModules").Remove(proxy);
    scene->UpdateVTKObjects();
    }

  // remove proxy from TimeKeeper.
  vtkSMProxy* timeKeeper = this->FindTimeKeeper(proxy->GetSession());
  vtkSMPropertyHelper(timeKeeper, "Views").Remove(proxy);
  timeKeeper->UpdateVTKObjects();

  // remove all representation proxies.
  const char* pnames[]={"Representations", "HiddenRepresentations",
    "Props", "HiddenProps", NULL};
  for (int index=0; unregister_representations && (pnames[index] != NULL); ++index)
    {
    vtkSMProperty* prop = proxy->GetProperty(pnames[index]);
    if (prop == NULL)
      {
      continue;
      }

    typedef std::vector<vtkWeakPointer<vtkSMProxy> > proxyvectortype;
    proxyvectortype reprs;

    vtkSMPropertyHelper helper(prop);
    for (unsigned int cc=0, max=helper.GetNumberOfElements(); cc < max ; ++cc)
      {
      reprs.push_back(helper.GetAsProxy(cc));
      }

    for (proxyvectortype::iterator iter=reprs.begin(), end=reprs.end(); iter!=end; ++iter)
      {
      if (iter->GetPointer())
        {
        this->UnRegisterProxy(iter->GetPointer());
        }
      }
    }

  // unregister dependencies.
  this->UnRegisterDependencies(proxy);

  // this will remove both proxy-list-domain helpers and animation helpers.
  this->FinalizeProxy(proxy);

  pxm->UnRegisterProxy("views", proxyname.c_str(), proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterRepresentationProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }

  // Register the proxy itself.
  proxy->GetSessionProxyManager()->RegisterProxy("representations", proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterRepresentationProxy(vtkSMProxy* proxy)
{
  PREPARE_FOR_UNREGISTERING(proxy);

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
  std::string groupname("representations");
  const char* _proxyname = pxm->GetProxyName(groupname.c_str(), proxy);
  if (_proxyname == NULL)
    {
    groupname = "scalar_bars";
    _proxyname = pxm->GetProxyName(groupname.c_str(), proxy);
    if (_proxyname == NULL)
      {
      return false;
      }
    }
  SM_SCOPED_TRACE(Delete).arg("proxy", proxy);
  const std::string proxyname(_proxyname);

  //---------------------------------------------------------------------------
  // remove the representation from any views.
  typedef std::vector<std::pair<vtkSMProxy*, vtkSMProperty*> > viewsvector;
  viewsvector views;
  for (unsigned int cc=0, max=proxy->GetNumberOfConsumers(); cc < max; cc++)
    {
    vtkSMProxy* consumer = proxy->GetConsumerProxy(cc);
    consumer = consumer? consumer->GetTrueParentProxy() : NULL;
    if (consumer && consumer->IsA("vtkSMViewProxy") && proxy->GetConsumerProperty(cc))
      {
      views.push_back(
        std::pair<vtkSMProxy*, vtkSMProperty*>(
          consumer, proxy->GetConsumerProperty(cc)));
      }
    }
  for (viewsvector::iterator iter=views.begin(), max=views.end(); iter != max; ++iter)
    {
    if (iter->first && iter->second)
      {
      vtkSMPropertyHelper(iter->second).Remove(proxy);
      iter->first->UpdateVTKObjects();
      }
    }

  // unregister dependencies.
  this->UnRegisterDependencies(proxy);

  // this will remove both proxy-list-domain helpers and animation helpers.
  this->FinalizeProxy(proxy);

  proxy->GetSessionProxyManager()->UnRegisterProxy(
    groupname.c_str(), proxyname.c_str(), proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterColorTransferFunctionProxy(
  vtkSMProxy* proxy, const char* proxyname)
{
  if (!proxy)
    {
    return false;
    }

  proxy->GetSessionProxyManager()->RegisterProxy(
    "lookup_tables", proxyname, proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterOpacityTransferFunction(
  vtkSMProxy* proxy, const char* proxyname)
{
  if (!proxy)
    {
    return false;
    }

  proxy->GetSessionProxyManager()->RegisterProxy(
    "piecewise_functions", proxyname, proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::RegisterAnimationProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }

  proxy->GetSessionProxyManager()->RegisterProxy("animation", proxy);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterAnimationProxy(vtkSMProxy* proxy)
{
  PREPARE_FOR_UNREGISTERING(proxy);

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
  const char* _proxyname = pxm->GetProxyName("animation", proxy);
  if (_proxyname == NULL)
    {
    return false;
    }
  SM_SCOPED_TRACE(Delete).arg("proxy", proxy);
  const std::string proxyname(_proxyname);

  //---------------------------------------------------------------------------
  // Animation proxies are typically added to some other animation proxy. We
  // need to remove it from that proxy.
  typedef std::pair<vtkWeakPointer<vtkSMProxy>, vtkWeakPointer<vtkSMProperty> > proxypairitemtype;
  typedef std::vector<proxypairitemtype> proxypairvectortype;
  proxypairvectortype consumers;
  for (unsigned int cc=0, max=proxy->GetNumberOfConsumers(); cc<max; ++cc)
    {
    vtkSMProxy* consumer = proxy->GetConsumerProxy(cc);
    consumer = consumer? consumer->GetTrueParentProxy() : NULL;
    if (proxy->GetConsumerProperty(cc) &&
      consumer && consumer->GetXMLGroup() &&
      strcmp(consumer->GetXMLGroup(), "animation") ==0)
      {
      consumers.push_back(proxypairitemtype(consumer, proxy->GetConsumerProperty(cc)));
      }
    }
  for (proxypairvectortype::iterator iter=consumers.begin(), max=consumers.end();
    iter != max; ++iter)
    {
    if (iter->first && iter->second)
      {
      vtkSMPropertyHelper(iter->second).Remove(proxy);
      iter->first->UpdateVTKObjects();
      }
    }

  //---------------------------------------------------------------------------
  // destroy keyframes, if there are any.
  typedef std::vector<vtkWeakPointer<vtkSMProxy> > proxyvectortype;
  proxyvectortype keyframes;
  if (vtkSMProperty* kfProperty = proxy->GetProperty("KeyFrames"))
    {
    vtkSMPropertyHelper helper(kfProperty);
    for (unsigned int cc=0, max=helper.GetNumberOfElements(); cc<max;++cc)
      {
      keyframes.push_back(helper.GetAsProxy(cc));
      }
    }

  // unregister dependencies.
  this->UnRegisterDependencies(proxy);

  // this will remove both proxy-list-domain helpers and animation helpers.
  this->FinalizeProxy(proxy);

  pxm->UnRegisterProxy("animation", proxyname.c_str(), proxy);

  // now destroy all keyframe proxies.
  for (proxyvectortype::iterator iter=keyframes.begin(), max=keyframes.end();
    iter != max;++iter)
    {
    if (iter->GetPointer())
      {
      this->UnRegisterProxy(iter->GetPointer());
      }
    }
  return true;
}

//----------------------------------------------------------------------------
void vtkSMParaViewPipelineController::UpdateSettingsProxies(vtkSMSession* session)
{
  // Set up the settings proxies
  vtkSMSessionProxyManager* pxm = session->GetSessionProxyManager();
  vtkSMProxyDefinitionManager* pdm = pxm->GetProxyDefinitionManager();
  vtkPVProxyDefinitionIterator* iter = pdm->NewSingleGroupIterator( "settings" );
  for (iter->GoToFirstItem(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
    {
    vtkSMProxy* proxy = pxm->GetProxy(iter->GetGroupName(), iter->GetProxyName());
    if (!proxy)
      {
      proxy = vtkSafeNewProxy(pxm, iter->GetGroupName(), iter->GetProxyName());
      if (proxy)
        {
        this->InitializeProxy(proxy);
        if (strcmp(iter->GetProxyName(), "RenderViewSettings") == 0)
          {
          this->HandleLZ4Issue(proxy);
          }
        pxm->RegisterProxy(iter->GetGroupName(), iter->GetProxyName(), proxy);
        proxy->UpdateVTKObjects();
        proxy->Delete();
        }
      }
    }
  iter->Delete();
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::PreInitializeProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }

  // 1. Load XML defaults
  //    (already done by NewProxy() call).

  // 2. Create proxies for proxy-list domains.
  if (!this->CreateProxiesForProxyListDomains(proxy))
    {
    return false;
    }

  // 3. Process global properties hints.
  if (!this->SetupGlobalPropertiesLinks(proxy))
    {
    return false;
    }

  // 4. Load property values from Settings.
  vtkSMSettings * settings = vtkSMSettings::GetInstance();
  settings->GetProxySettings(proxy);

  // Now, update the initialization time.
  this->Internals->InitializationTimeStamps[proxy].Modified();

  // Note: we need to be careful with *** vtkSMCompoundSourceProxy ***
  return true;
}

//----------------------------------------------------------------------------
unsigned long vtkSMParaViewPipelineController::GetInitializationTime(vtkSMProxy* proxy)
{
  vtkInternals::TimeStampsMap::iterator titer =
    this->Internals->InitializationTimeStamps.find(proxy);
  return (titer == this->Internals->InitializationTimeStamps.end()? 0: titer->second);
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::PostInitializeProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }

  vtkInternals::TimeStampsMap::iterator titer =
    this->Internals->InitializationTimeStamps.find(proxy);
  if (titer == this->Internals->InitializationTimeStamps.end())
    {
    vtkErrorMacro(
      "PostInitializeProxy must only be called after PreInitializeProxy");
    return false;
    }

  assert(titer != this->Internals->InitializationTimeStamps.end());

  vtkTimeStamp ts = titer->second;
  this->Internals->InitializationTimeStamps.erase(titer);

  // ensure everything is up-to-date.
  proxy->UpdateVTKObjects();

  // FIXME: need to figure out how we should really deal with this.
  // We don't reset properties on custom filter.
  if (!proxy->IsA("vtkSMCompoundSourceProxy"))
    {
    if (vtkSMSourceProxy* sourceProxy = vtkSMSourceProxy::SafeDownCast(proxy))
      {
      // Since domains depend on information properties, it's essential we update
      // property information first.
      if (sourceProxy->GetNumberOfOutputPorts() > 0)
        {
        // This is only done for non-writers, or non-sinks.
        sourceProxy->UpdatePipelineInformation();
        }
      }

    // Reset property values using domains. However, this should only be done for
    // properties that were not modified between the PreInitializePipelineProxy and
    // PostInitializePipelineProxy calls.

    vtkSmartPointer<vtkSMPropertyIterator> iter;
    iter.TakeReference(proxy->NewPropertyIterator());

    // iterate over properties and reset them to default. if any property says its
    // domain is modified after we reset it, we need to reset it again since its
    // default may have changed.
    vtkDomainObserver observer;

    for (iter->Begin(); !iter->IsAtEnd(); iter->Next())
      {
      vtkSMProperty* smproperty = iter->GetProperty();

      if ((smproperty->GetMTime() > ts) ||
        smproperty->GetInformationOnly() ||
        // doesn't make sense to set property values for internal properties.
        smproperty->GetIsInternal())
        {
        // Property was modified between since the PreInitializeProxy() call. We
        // leave it untouched.
        continue;
        }

      vtkPVXMLElement* propHints = smproperty->GetHints();
      if (propHints && propHints->FindNestedElementByName("NoDefault"))
        {
        // Don't reset properties that request overriding of the default mechanism.
        continue;
        }

      observer.Monitor(iter->GetProperty());
      iter->GetProperty()->ResetToDomainDefaults();
      }

    const std::set<vtkSMProperty*> &props =
      observer.GetPropertiesWithModifiedDomains();
    for (std::set<vtkSMProperty*>::const_iterator siter = props.begin(); siter != props.end(); ++siter)
      {
      (*siter)->ResetToDomainDefaults();
      }
    proxy->UpdateVTKObjects();
    }

  // Register proxies created for proxy list domains.
  this->RegisterProxiesForProxyListDomains(proxy);

  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::FinalizeProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }

  //---------------------------------------------------------------------------
  // Unregister all helper proxies. This will "unregister" all the helper
  // proxies which ensures that any "dependent" proxies for those helpers
  // also get removed.
  std::string groupname = this->GetHelperProxyGroupName(proxy);

  typedef std::pair<std::string, vtkWeakPointer<vtkSMProxy> > proxymapitemtype;
  typedef std::vector<proxymapitemtype> proxymaptype;
  proxymaptype proxymap;

  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
    {
    vtkNew<vtkSMProxyIterator> iter;
    iter->SetSessionProxyManager(pxm);
    iter->SetModeToOneGroup();
    for (iter->Begin(groupname.c_str()); !iter->IsAtEnd(); iter->Next())
      {
      proxymap.push_back(proxymapitemtype(
          iter->GetKey(), iter->GetProxy()));
      }
    }

  for (proxymaptype::iterator iter = proxymap.begin(), end = proxymap.end();
    iter != end; ++iter)
    {
    if (iter->second)
      {
      // remove any dependencies for this proxy.
      this->UnRegisterDependencies(iter->second);
      // remove any helpers for this proxy.
      this->FinalizeProxy(iter->second);
      // unregister the proxy.
      pxm->UnRegisterProxy(
        groupname.c_str(), iter->first.c_str(), iter->second);
      }
    }

  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterDependencies(vtkSMProxy* proxy)
{
  assert(proxy != NULL);

  //---------------------------------------------------------------------------
  // Before going any further build a list of all consumer proxies
  // (not pointing to internal/sub-proxies).

  typedef std::vector<vtkWeakPointer<vtkSMProxy> > proxyvectortype;
  proxyvectortype consumers;

  for (unsigned int cc=0, max = proxy->GetNumberOfConsumers(); cc < max; ++cc)
    {
    vtkSMProxy* consumer = proxy->GetConsumerProxy(cc);
    consumer = consumer? consumer->GetTrueParentProxy() : NULL;
    if (consumer)
      {
      consumers.push_back(consumer);
      }
    }

  //---------------------------------------------------------------------------
  for (proxyvectortype::iterator iter = consumers.begin(), max = consumers.end();
    iter != max; ++iter)
    {
    vtkSMProxy* consumer = iter->GetPointer();
    if (!consumer)
      {
      continue;
      }

    if (
      // Remove representations connected to this proxy, if any.
      consumer->IsA("vtkSMRepresentationProxy") ||

      // Remove any animation cues for this proxy
      strcmp(consumer->GetXMLGroup(), "animation") == 0 ||

      false)
      {
      this->UnRegisterProxy(consumer);
      }
    }
  //---------------------------------------------------------------------------

  // TODO: remove any property links/proxy link setup for this proxy.

  return true;
}

//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineController::UnRegisterProxy(vtkSMProxy* proxy)
{
  if (!proxy)
    {
    return false;
    }


  SM_SCOPED_TRACE(CleanupAccessor).arg("proxy", proxy);

  // determine what type of proxy is this, based on that, we can finalize it.
  vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
  if (pxm->GetProxyName("sources", proxy))
    {
    return this->UnRegisterPipelineProxy(proxy);
    }
  else if (pxm->GetProxyName("representations", proxy) ||
           pxm->GetProxyName("scalar_bars", proxy))
    {
    return this->UnRegisterRepresentationProxy(proxy);
    }
  else if (pxm->GetProxyName("views", proxy))
    {
    return this->UnRegisterViewProxy(proxy);
    }
  else if (pxm->GetProxyName("animation", proxy))
    {
    if (proxy != this->GetAnimationScene(proxy->GetSession()))
      {
      return this->UnRegisterAnimationProxy(proxy);
      }
    }
  else
    {
    PREPARE_FOR_UNREGISTERING(proxy);

    const char* known_groups[] = {
      "lookup_tables", "piecewise_functions", "layouts", NULL };
    for (int cc=0; known_groups[cc] != NULL; ++cc)
      {
      if (const char* pname = pxm->GetProxyName(known_groups[cc], proxy))
        {
        SM_SCOPED_TRACE(Delete).arg("proxy", proxy);

        // unregister dependencies.
        if (!this->UnRegisterDependencies(proxy))
          {
          return false;
          }

        // this will remove both proxy-list-domain helpers and animation helpers.
        if (!this->FinalizeProxy(proxy))
          {
          return false;
          }
        pxm->UnRegisterProxy(known_groups[cc], pname, proxy);
        return true;
        }
      }
    }
  return false;
}

//----------------------------------------------------------------------------
void vtkSMParaViewPipelineController::HandleLZ4Issue(vtkSMProxy* renderViewSettings)
{
  vtkSMProperty* compressorConfig = renderViewSettings->GetProperty("CompressorConfig");
  if (!compressorConfig)
    {
    return;
    }

  if (vtkPVXMLElement* hints = compressorConfig->GetHints())
    {
    if (hints->FindNestedElementByName("SupportsLZ4") != NULL)
      {
      return;
      }
    }

  // We're dealing with an server that doesn't support LZ4. If the default is to
  // use LZ4 change it to not use LZ4.
  vtkSMPropertyHelper helper(compressorConfig);
  if (strncmp(helper.GetAsString(), "vtkLZ4Compressor", strlen("vtkLZ4Compressor")) != 0)
    {
    return;
    }
  std::string newvalue("vtkSquirtCompressor");
  newvalue += helper.GetAsString() + strlen("vtkLZ4Compressor");
  helper.Set(newvalue.c_str());
  vtkWarningMacro(
    "You have connected to a server that doesn't support 'LZ4' compression for delivering "
    "remotely rendered images to the client. 'LZ4' is your current default "
    "(and recommended) compression mode. Switching to 'SQUIRT'. "
    "Remote rendering that uses translucent surfaces or volumes will have artifacts "
    "with SQUIRT. Switch to zlib or no compression to avoid those, if needed.");
  renderViewSettings->UpdateVTKObjects();
}

//----------------------------------------------------------------------------
void vtkSMParaViewPipelineController::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
