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

  Program:   Visualization Toolkit
  Module:    vtkObject.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkObject.h"

#include "vtkCommand.h"
#include "vtkDebugLeaks.h"
#include "vtkGarbageCollector.h"
#include "vtkObjectFactory.h"
#include "vtkTimeStamp.h"
#include "vtkWeakPointer.h"

#include <algorithm>
#include <vector>

// Initialize static member that controls warning display
static int vtkObjectGlobalWarningDisplay = 1;


//----------------------------------------------------------------------------
// avoid dll boundary problems
#ifdef _WIN32
void* vtkObject::operator new(size_t nSize)
{
  void* p=malloc(nSize);
  return p;
}

//----------------------------------------------------------------------------
void vtkObject::operator delete( void *p )
{
  free(p);
}
#endif

//----------------------------------------------------------------------------
void vtkObject::SetGlobalWarningDisplay(int val)
{
  vtkObjectGlobalWarningDisplay = val;
}

//----------------------------------------------------------------------------
int vtkObject::GetGlobalWarningDisplay()
{
  return vtkObjectGlobalWarningDisplay;
}

//----------------------------------Command/Observer stuff-------------------
// The Command/Observer design pattern is used to invoke and dispatch events.
// The class vtkSubjectHelper keeps a list of observers (which in turn keep
// an instance of vtkCommand) which respond to registered events.
//
class vtkObserver
{
 public:
  vtkObserver():Command(nullptr),Event(0),Tag(0),Next(nullptr),Priority(0.0) {}
  ~vtkObserver();
  void PrintSelf(ostream& os, vtkIndent indent);

  vtkCommand *Command;
  unsigned long Event;
  unsigned long Tag;
  vtkObserver *Next;
  float Priority;
};

void vtkObserver::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "vtkObserver (" << this << ")\n";
  indent = indent.GetNextIndent();
  os << indent << "Event: " << this->Event << "\n";
  os << indent << "EventName: " << vtkCommand::GetStringFromEventId(this->Event) << "\n";
  os << indent << "Command: " << this->Command << "\n";
  os << indent << "Priority: " << this->Priority << "\n";
  os << indent << "Tag: " << this->Tag << "\n";
}

//----------------------------------------------------------------------------
// The vtkSubjectHelper keeps a list of observers and dispatches events to them.
// It also invokes the vtkCommands associated with the observers. Currently
// vtkSubjectHelper is an internal class to vtkObject. However, due to requirements
// from the VTK widgets it may be necessary to break out the vtkSubjectHelper at
// some point (for reasons of event management, etc.)
class vtkSubjectHelper
{
public:
  vtkSubjectHelper():ListModified(0),Focus1(nullptr),Focus2(nullptr),Start(nullptr),Count(1) {}
  ~vtkSubjectHelper();

  unsigned long AddObserver(unsigned long event, vtkCommand *cmd, float p);
  void RemoveObserver(unsigned long tag);
  void RemoveObservers(unsigned long event);
  void RemoveObservers(unsigned long event, vtkCommand *cmd);
  void RemoveAllObservers();
  int InvokeEvent(unsigned long event, void *callData, vtkObject *self);
  vtkCommand *GetCommand(unsigned long tag);
  unsigned long GetTag(vtkCommand*);
  int HasObserver(unsigned long event);
  int HasObserver(unsigned long event, vtkCommand *cmd);
  void GrabFocus(vtkCommand *c1, vtkCommand *c2) {this->Focus1 = c1; this->Focus2 = c2;}
  void ReleaseFocus() {this->Focus1 = nullptr; this->Focus2 = nullptr;}
  void PrintSelf(ostream& os, vtkIndent indent);

  int         ListModified;

  // This is to support the GrabFocus() methods found in vtkInteractorObserver.
  vtkCommand *Focus1;
  vtkCommand *Focus2;

protected:
  vtkObserver  *Start;
  unsigned long Count;
};

// ------------------------------------vtkObject----------------------

vtkObject *vtkObject::New()
{
  vtkObject *ret = new vtkObject;
  ret->InitializeObjectBase();
  return ret;
}


//----------------------------------------------------------------------------
// Create an object with Debug turned off and modified time initialized
// to zero.
vtkObject::vtkObject()
{
  this->Debug = false;
  this->SubjectHelper = nullptr;
  this->Modified(); // Insures modified time > than any other time
  // initial reference count = 1 and reference counting on.
}

//----------------------------------------------------------------------------
vtkObject::~vtkObject()
{
  vtkDebugMacro(<< "Destructing!");

  // warn user if reference counting is on and the object is being referenced
  // by another object
  if ( this->ReferenceCount > 0)
  {
    vtkErrorMacro(<< "Trying to delete object with non-zero reference count.");
  }
  delete this->SubjectHelper;
  this->SubjectHelper = nullptr;
}

//----------------------------------------------------------------------------
// Return the modification for this object.
vtkMTimeType vtkObject::GetMTime()
{
  return this->MTime.GetMTime();
}

// Chaining method to print an object's instance variables, as well as
// its superclasses.
void vtkObject::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "Debug: " << (this->Debug ? "On\n" : "Off\n");
  os << indent << "Modified Time: " << this->GetMTime() << "\n";
  this->Superclass::PrintSelf(os, indent);
  os << indent << "Registered Events: ";
  if ( this->SubjectHelper )
  {
    os << endl;
    this->SubjectHelper->PrintSelf(os,indent.GetNextIndent());
  }
  else
  {
    os << "(none)\n";
  }
}

//----------------------------------------------------------------------------
// Turn debugging output on.
// The Modified() method is purposely not called since we do not want to affect
// the modification time when enabling debug output.
void vtkObject::DebugOn()
{
  this->Debug = true;
}

//----------------------------------------------------------------------------
// Turn debugging output off.
void vtkObject::DebugOff()
{
  this->Debug = false;
}

//----------------------------------------------------------------------------
// Get the value of the debug flag.
bool vtkObject::GetDebug()
{
  return this->Debug;
}

//----------------------------------------------------------------------------
// Set the value of the debug flag. A true value turns debugging on.
void vtkObject::SetDebug(bool debugFlag)
{
  this->Debug = debugFlag;
}


//----------------------------------------------------------------------------
// This method is called when vtkErrorMacro executes. It allows
// the debugger to break on error.
void vtkObject::BreakOnError()
{
}

//----------------------------------Command/Observer stuff-------------------
//

//----------------------------------------------------------------------------
vtkObserver::~vtkObserver()
{
  this->Command->UnRegister(nullptr);
}

//----------------------------------------------------------------------------
vtkSubjectHelper::~vtkSubjectHelper()
{
  vtkObserver *elem = this->Start;
  vtkObserver *next;
  while (elem)
  {
    next = elem->Next;
    delete elem;
    elem = next;
  }
  this->Start = nullptr;
  this->Focus1 = nullptr;
  this->Focus2 = nullptr;
}


//----------------------------------------------------------------------------
unsigned long vtkSubjectHelper::
AddObserver(unsigned long event, vtkCommand *cmd, float p)
{
  vtkObserver *elem;

  // initialize the new observer element
  elem = new vtkObserver;
  elem->Priority = p;
  elem->Next = nullptr;
  elem->Event = event;
  elem->Command = cmd;
  cmd->Register(nullptr);
  elem->Tag = this->Count;
  this->Count++;

  // now insert into the list
  // if no other elements in the list then this is Start
  if (!this->Start)
  {
    this->Start = elem;
  }
  else
  {
    // insert high priority first
    vtkObserver* prev = nullptr;
    vtkObserver* pos = this->Start;
    while(pos->Priority >= elem->Priority && pos->Next)
    {
      prev = pos;
      pos = pos->Next;
    }
    // pos is Start and elem should not be start
    if(pos->Priority > elem->Priority)
    {
      pos->Next = elem;
    }
    else
    {
      if(prev)
      {
        prev->Next = elem;
      }
      elem->Next = pos;
      // check to see if the new element is the start
      if(pos == this->Start)
      {
        this->Start = elem;
      }
    }
  }
  return elem->Tag;
}

//----------------------------------------------------------------------------
void vtkSubjectHelper::RemoveObserver(unsigned long tag)
{
  vtkObserver *elem;
  vtkObserver *prev;
  vtkObserver *next;

  elem = this->Start;
  prev = nullptr;
  while (elem)
  {
    if (elem->Tag == tag)
    {
      if (prev)
      {
        prev->Next = elem->Next;
        next = prev->Next;
      }
      else
      {
        this->Start = elem->Next;
        next = this->Start;
      }
      delete elem;
      elem = next;
    }
    else
    {
      prev = elem;
      elem = elem->Next;
    }
  }

  this->ListModified = 1;
}

//----------------------------------------------------------------------------
void vtkSubjectHelper::RemoveObservers(unsigned long event)
{
  vtkObserver *elem;
  vtkObserver *prev;
  vtkObserver *next;

  elem = this->Start;
  prev = nullptr;
  while (elem)
  {
    if (elem->Event == event)
    {
      if (prev)
      {
        prev->Next = elem->Next;
        next = prev->Next;
      }
      else
      {
        this->Start = elem->Next;
        next = this->Start;
      }
      delete elem;
      elem = next;
    }
    else
    {
      prev = elem;
      elem = elem->Next;
    }
  }

  this->ListModified = 1;
}

//----------------------------------------------------------------------------
void vtkSubjectHelper::RemoveObservers(unsigned long event, vtkCommand *cmd)
{
  vtkObserver *elem;
  vtkObserver *prev;
  vtkObserver *next;

  elem = this->Start;
  prev = nullptr;
  while (elem)
  {
    if (elem->Event == event && elem->Command == cmd)
    {
      if (prev)
      {
        prev->Next = elem->Next;
        next = prev->Next;
      }
      else
      {
        this->Start = elem->Next;
        next = this->Start;
      }
      delete elem;
      elem = next;
    }
    else
    {
      prev = elem;
      elem = elem->Next;
    }
  }

  this->ListModified = 1;
}

//----------------------------------------------------------------------------
void vtkSubjectHelper::RemoveAllObservers()
{
  vtkObserver *elem = this->Start;
  vtkObserver *next;
  while (elem)
  {
    next = elem->Next;
    delete elem;
    elem = next;
  }
  this->Start = nullptr;
}

//----------------------------------------------------------------------------
int vtkSubjectHelper::HasObserver(unsigned long event)
{
  vtkObserver *elem = this->Start;
  while (elem)
  {
    if (elem->Event == event || elem->Event == vtkCommand::AnyEvent)
    {
      return 1;
    }
    elem = elem->Next;
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkSubjectHelper::HasObserver(unsigned long event, vtkCommand *cmd)
{
  vtkObserver *elem = this->Start;
  while (elem)
  {
    if ((elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
        elem->Command == cmd)
    {
      return 1;
    }
    elem = elem->Next;
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkSubjectHelper::InvokeEvent(unsigned long event, void *callData,
                                   vtkObject *self)
{
  int focusHandled = 0;

  // When we invoke an event, the observer may add or remove observers.  To make
  // sure that the iteration over the observers goes smoothly, we capture any
  // change to the list with the ListModified ivar.  However, an observer may
  // also do something that causes another event to be invoked in this object.
  // That means that this method will be called recursively, which means that we
  // will obliterate the ListModified flag that the first call is relying on.
  // To get around this, save the previous ListModified value on the stack and
  // then restore it before leaving.
  int saveListModified = this->ListModified;
  this->ListModified = 0;

  // We also need to save what observers we have called on the stack (lest it
  // get overridden in the event invocation).  Also make sure that we do not
  // invoke any new observers that were added during another observer's
  // invocation.
  typedef std::vector<unsigned long> VisitedListType;
  VisitedListType visited;
  vtkObserver *elem = this->Start;
  // If an element with a tag greater than maxTag is found, that means it has
  // been added after InvokeEvent is called (as a side effect of calling an
  // element command. In that case, the element is discarded and not executed.
  const unsigned long maxTag = this->Count;

  // Loop two or three times, giving preference to passive observers
  // and focus holders, if any.
  //
  // 0. Passive observer loop
  //   Loop over all observers and execute those that are passive observers.
  //   These observers should not affect the state of the system in any way,
  //   and should not be allowed to abort the event.
  //
  // 1. Focus loop
  //   If there is a focus holder, loop over all observers and execute
  //   those associated with either focus holder. Set focusHandled to
  //   indicate that a focus holder handled the event.
  //
  // 2. Remainder loop
  //   If no focus holder handled the event already, loop over the
  //   remaining observers. This loop will always get executed when there
  //   is no focus holder.

  // 0. Passive observer loop
  //
  vtkObserver *next;
  while (elem)
  {
    // store the next pointer because elem could disappear due to Command
    next = elem->Next;
    if (elem->Command->GetPassiveObserver() &&
        (elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
        elem->Tag < maxTag)
    {
      VisitedListType::iterator vIter =
        std::lower_bound(visited.begin(), visited.end(), elem->Tag);
      if (vIter == visited.end() || *vIter != elem->Tag)
      {
        // Sorted insertion by tag to speed-up future searches at limited
        // insertion cost because it reuses the search iterator already at the
        // correct location
        visited.insert(vIter, elem->Tag);
        vtkCommand* command = elem->Command;
        command->Register(command);
        elem->Command->Execute(self,event,callData);
        command->UnRegister();
      }
    }
    if (this->ListModified)
    {
      vtkGenericWarningMacro(<<"Passive observer should not call AddObserver or RemoveObserver in callback.");
      elem = this->Start;
      this->ListModified = 0;
    }
    else
    {
      elem = next;
    }
  }

  // 1. Focus loop
  //
  if (this->Focus1 || this->Focus2)
  {
    elem = this->Start;
    while (elem)
    {
      // store the next pointer because elem could disappear due to Command
      next = elem->Next;
      if (((this->Focus1 == elem->Command) || (this->Focus2 == elem->Command)) &&
          (elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
          elem->Tag < maxTag)
      {
        VisitedListType::iterator vIter =
          std::lower_bound(visited.begin(), visited.end(), elem->Tag);
        if (vIter == visited.end() || *vIter != elem->Tag)
        {
          // Don't execute the remainder loop
          focusHandled = 1;
          // Sorted insertion by tag to speed-up future searches at limited
          // insertion cost because it reuses the search iterator already at the
          // correct location
          visited.insert(vIter, elem->Tag);
          vtkCommand* command = elem->Command;
          command->Register(command);
          command->SetAbortFlag(0);
          elem->Command->Execute(self,event,callData);
          // if the command set the abort flag, then stop firing events
          // and return
          if(command->GetAbortFlag())
          {
            command->UnRegister();
            this->ListModified = saveListModified;
            return 1;
          }
          command->UnRegister();
        }
      }
      if (this->ListModified)
      {
        elem = this->Start;
        this->ListModified = 0;
      }
      else
      {
        elem = next;
      }
    }
  }

  // 2. Remainder loop
  //
  if (!focusHandled)
  {
    elem = this->Start;
    while (elem)
    {
      // store the next pointer because elem could disappear due to Command
      next = elem->Next;
      if ((elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
          elem->Tag < maxTag)
      {
        VisitedListType::iterator vIter =
          std::lower_bound(visited.begin(), visited.end(), elem->Tag);
        if (vIter == visited.end() || *vIter != elem->Tag)
        {
          // Sorted insertion by tag to speed-up future searches at limited
          // insertion cost because it reuses the search iterator already at the
          // correct location
          visited.insert(vIter, elem->Tag);
          vtkCommand* command = elem->Command;
          command->Register(command);
          command->SetAbortFlag(0);
          elem->Command->Execute(self,event,callData);
          // if the command set the abort flag, then stop firing events
          // and return
          if(command->GetAbortFlag())
          {
            command->UnRegister();
            this->ListModified = saveListModified;
            return 1;
          }
          command->UnRegister();
        }
      }
      if (this->ListModified)
      {
        elem = this->Start;
        this->ListModified = 0;
      }
      else
      {
        elem = next;
      }
    }
  }

  this->ListModified = saveListModified;
  return 0;
}

//----------------------------------------------------------------------------
unsigned long vtkSubjectHelper::GetTag(vtkCommand* cmd)
{
  vtkObserver *elem = this->Start;
  while (elem)
  {
    if (elem->Command == cmd)
    {
      return elem->Tag;
    }
    elem = elem->Next;
  }
  return 0;
}

//----------------------------------------------------------------------------
vtkCommand *vtkSubjectHelper::GetCommand(unsigned long tag)
{
  vtkObserver *elem = this->Start;
  while (elem)
  {
    if (elem->Tag == tag)
    {
      return elem->Command;
    }
    elem = elem->Next;
  }
  return nullptr;
}

//----------------------------------------------------------------------------
void vtkSubjectHelper::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "Registered Observers:\n";
  indent = indent.GetNextIndent();
  vtkObserver *elem = this->Start;
  if ( !elem )
  {
    os << indent << "(none)\n";
    return;
  }

  for ( ; elem; elem=elem->Next )
  {
    elem->PrintSelf(os, indent);
  }
}

//--------------------------------vtkObject observer-----------------------
unsigned long vtkObject::AddObserver(unsigned long event, vtkCommand *cmd, float p)
{
  if (!this->SubjectHelper)
  {
    this->SubjectHelper = new vtkSubjectHelper;
  }
  return this->SubjectHelper->AddObserver(event,cmd, p);
}

//----------------------------------------------------------------------------
unsigned long vtkObject::AddObserver(const char *event,vtkCommand *cmd, float p)
{
  return this->AddObserver(vtkCommand::GetEventIdFromString(event), cmd, p);
}

//----------------------------------------------------------------------------
vtkCommand *vtkObject::GetCommand(unsigned long tag)
{
  if (this->SubjectHelper)
  {
    return this->SubjectHelper->GetCommand(tag);
  }
  return nullptr;
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObserver(unsigned long tag)
{
  if (this->SubjectHelper)
  {
    this->SubjectHelper->RemoveObserver(tag);
  }
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObserver(vtkCommand* c)
{
  if (this->SubjectHelper)
  {
    unsigned long tag = this->SubjectHelper->GetTag(c);
    while(tag)
    {
      this->SubjectHelper->RemoveObserver(tag);
      tag = this->SubjectHelper->GetTag(c);
    }
  }
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObservers(unsigned long event)
{
  if (this->SubjectHelper)
  {
    this->SubjectHelper->RemoveObservers(event);
  }
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObservers(const char *event)
{
  this->RemoveObservers(vtkCommand::GetEventIdFromString(event));
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObservers(unsigned long event, vtkCommand *cmd)
{
  if (this->SubjectHelper)
  {
    this->SubjectHelper->RemoveObservers(event, cmd);
  }
}

//----------------------------------------------------------------------------
void vtkObject::RemoveObservers(const char *event, vtkCommand *cmd)
{
  this->RemoveObservers(vtkCommand::GetEventIdFromString(event), cmd);
}

//----------------------------------------------------------------------------
void vtkObject::RemoveAllObservers()
{
  if ( this->SubjectHelper )
  {
    this->SubjectHelper->RemoveAllObservers();
  }
}

//----------------------------------------------------------------------------
int vtkObject::InvokeEvent(unsigned long event, void *callData)
{
  if (this->SubjectHelper)
  {
    return this->SubjectHelper->InvokeEvent(event,callData, this);
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkObject::InvokeEvent(const char *event, void *callData)
{
  return this->InvokeEvent(vtkCommand::GetEventIdFromString(event), callData);
}

//----------------------------------------------------------------------------
int vtkObject::HasObserver(unsigned long event)
{
  if (this->SubjectHelper)
  {
    return this->SubjectHelper->HasObserver(event);
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkObject::HasObserver(const char *event)
{
  return this->HasObserver(vtkCommand::GetEventIdFromString(event));
}

//----------------------------------------------------------------------------
int vtkObject::HasObserver(unsigned long event, vtkCommand *cmd)
{
  if (this->SubjectHelper)
  {
    return this->SubjectHelper->HasObserver(event, cmd);
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkObject::HasObserver(const char *event, vtkCommand *cmd)
{
  return this->HasObserver(vtkCommand::GetEventIdFromString(event), cmd);
}

//----------------------------------------------------------------------------
void vtkObject::InternalGrabFocus(vtkCommand *mouseEvents, vtkCommand *keypressEvents)
{
  if (this->SubjectHelper)
  {
    this->SubjectHelper->GrabFocus(mouseEvents,keypressEvents);
  }
}

//----------------------------------------------------------------------------
void vtkObject::InternalReleaseFocus()
{
  if (this->SubjectHelper)
  {
    this->SubjectHelper->ReleaseFocus();
  }
}


//----------------------------------------------------------------------------
void vtkObject::Modified()
{
  this->MTime.Modified();
  this->InvokeEvent(vtkCommand::ModifiedEvent,nullptr);
}

//----------------------------------------------------------------------------
void vtkObject::RegisterInternal(vtkObjectBase* o, vtkTypeBool check)
{
  // Print debugging messages.
  if(o)
  {
    vtkDebugMacro(<< "Registered by " << o->GetClassName() << " (" << o
                  << "), ReferenceCount = " << this->ReferenceCount+1);
  }
  else
  {
    vtkDebugMacro(<< "Registered by nullptr, ReferenceCount = "
                  << this->ReferenceCount+1);
  }

  // Increment the reference count.
  this->Superclass::RegisterInternal(o, check);
}

//----------------------------------------------------------------------------
void vtkObject::UnRegisterInternal(vtkObjectBase* o, vtkTypeBool check)
{
  // Print debugging messages.
  if(o)
  {
    vtkDebugMacro(<< "UnRegistered by "
                  << o->GetClassName() << " (" << o << "), ReferenceCount = "
                  << (this->ReferenceCount-1));
  }
  else
  {
    vtkDebugMacro(<< "UnRegistered by nullptr, ReferenceCount = "
                  << (this->ReferenceCount-1));
  }

  if(this->ReferenceCount == 1)
  {
    // The reference count is 1, so the object is about to be deleted.
    // Invoke the delete event.
    this->InvokeEvent(vtkCommand::DeleteEvent, nullptr);

    // Clean out observers prior to entering destructor
    this->RemoveAllObservers();
  }

  // Decrement the reference count.
  this->Superclass::UnRegisterInternal(o, check);
}

//----------------------------------------------------------------------------
// Internal observer used by vtkObject::AddTemplatedObserver to add a
// vtkClassMemberCallbackBase instance as an observer to an event.
class vtkObjectCommandInternal : public vtkCommand
{
  vtkObject::vtkClassMemberCallbackBase* Callable;
public:
  static vtkObjectCommandInternal* New()
    { return new vtkObjectCommandInternal(); }

  vtkTypeMacro(vtkObjectCommandInternal, vtkCommand);
  void Execute(
    vtkObject *caller, unsigned long eventId, void *callData) override
  {
    if (this->Callable)
    {
      this->AbortFlagOff();
      if((*this->Callable)(caller, eventId, callData))
      {
        this->AbortFlagOn();
      }
    }
  }

  // Takes over the ownership of \c callable.
  void SetCallable(vtkObject::vtkClassMemberCallbackBase* callable)
  {
    delete this->Callable;
    this->Callable = callable;
  }

protected:
  vtkObjectCommandInternal()
  {
    this->Callable = nullptr;
  }
  ~vtkObjectCommandInternal() override
  {
    delete this->Callable;
  }
};

//----------------------------------------------------------------------------
unsigned long vtkObject::AddTemplatedObserver(
  unsigned long event, vtkObject::vtkClassMemberCallbackBase* callable, float priority)
{
  vtkObjectCommandInternal* command = vtkObjectCommandInternal::New();
  // Takes over the ownership of \c callable.
  command->SetCallable(callable);
  unsigned long id = this->AddObserver(event, command, priority);
  command->Delete();
  return id;
}
