text: /*=========================================================================
  
    Program:   ParaView
    Module:    vtkSMProxyProperty.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 "vtkSMProxyProperty.h"
  
  #include "vtkClientServerStream.h"
  #include "vtkObjectFactory.h"
  #include "vtkPVXMLElement.h"
  #include "vtkSmartPointer.h"
  #include "vtkSMDomainIterator.h"
  #include "vtkSMMessage.h"
  #include "vtkSMProxyGroupDomain.h"
  #include "vtkSMProxy.h"
  #include "vtkSMProxyLocator.h"
  #include "vtkSMProxyManager.h"
  #include "vtkSMStateLocator.h"
  #include "vtkSMSession.h"
  #include "vtkSMSessionProxyManager.h"
  #include "vtkStdString.h"
  #include "vtkCommand.h"
  
  #include <algorithm>
  #include <map>
  #include <set>
  #include <vector>
  #include <iterator>
  
  #include <assert.h>
  
  
  //***************************************************************************
  //                          Internal classes
  //***************************************************************************
  class vtkSMProxyProperty::vtkProxyPointer
  {
  public:
    vtkSmartPointer<vtkSMProxy> Value;
    vtkSMProxyProperty* Self;
    vtkProxyPointer()
      {
      this->Self = NULL;
      this->Value = NULL;
      }
  
    vtkProxyPointer(vtkSMProxyProperty* self, vtkSMProxy* value)
      {
      this->Self = self;
      this->Value = value;
      if (value && self)
        {
        self->AddProducer(this->Value);
        }
      }
  
    ~vtkProxyPointer()
      {
      if (this->Self && this->Value)
        {
        this->Self->RemoveProducer(this->Value);
        }
      }
  
    vtkProxyPointer(const vtkProxyPointer& other)
      {
      this->Self = other.Self;
      this->Value = other.Value;
      if (this->Self && this->Value)
        {
        this->Self->AddProducer(this->Value);
        }
      }
  
    vtkProxyPointer& operator=(const vtkProxyPointer& other)
      {
      if (this->Self && this->Value)
        {
        this->Self->RemoveProducer(this->Value);
        }
      this->Self = other.Self;
      this->Value = other.Value;
      if (this->Self && this->Value)
        {
        this->Self->AddProducer(this->Value);
        }
      return *this;
      }
  
    bool operator==(const vtkProxyPointer& other) const
      {
      return (this->Self == other.Self && this->Value == other.Value);
      }
  };
  //***************************************************************************
  struct vtkSMProxyPropertyInternals
  {
    typedef std::vector<vtkSMProxyProperty::vtkProxyPointer> VectorOfProxies;
  
    void PrintProxy()
      {
      cout << "ProxyList: ";
      VectorOfProxies::iterator iter;
      for(iter = Proxies.begin(); iter != Proxies.end(); iter++)
        {
        cout << " " << (iter->Value->HasGlobalID() ? iter->Value->GetGlobalID() : 0);
        }
      cout << endl;
      }
  
    VectorOfProxies Proxies;
    std::vector<vtkSMProxy*> UncheckedProxies;
    std::map<void*, int> ProducerCounts;
  };
  //***************************************************************************
  vtkStandardNewMacro(vtkSMProxyProperty);
  bool vtkSMProxyProperty::CreateProxyAllowed = false; // static init
  //---------------------------------------------------------------------------
  vtkSMProxyProperty::vtkSMProxyProperty()
  {
    this->PPInternals = new vtkSMProxyPropertyInternals;
    this->SkipDependency = false;
  }
  
  //---------------------------------------------------------------------------
  vtkSMProxyProperty::~vtkSMProxyProperty()
  {
    // Ensure that this happens before we delete this->PPInternals. This is needed
    // because when items in this vector are being destroyed, they call
    // vtkSMProxyProperty::RemoveProducer(). That function uses this->PPInternals
    // to update producer counts.
    this->PPInternals->Proxies.clear();
  
    // We use this indirection to detect any other issues we may run into when
    // destroying the vtkSMProxyPropertyInternals instance more
    // cleanly (since we'll be accessing a NULL ptr).
    vtkSMProxyPropertyInternals* temp = this->PPInternals;
    this->PPInternals = NULL;
    delete temp;
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::AddUncheckedProxy(vtkSMProxy* proxy)
  {
    this->PPInternals->UncheckedProxies.push_back(proxy);
    this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
  }
  
  //---------------------------------------------------------------------------
  unsigned int vtkSMProxyProperty::RemoveUncheckedProxy(vtkSMProxy* proxy)
  {
    std::vector<vtkSMProxy* >::iterator it =
      this->PPInternals->UncheckedProxies.begin();
    unsigned int idx = 0;
    for (; 
         it != this->PPInternals->UncheckedProxies.end(); 
         it++, idx++)
      {
      if (*it == proxy)
        {
        this->PPInternals->UncheckedProxies.erase(it);
        this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
        break;
        }
      }
    return idx;
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::SetUncheckedProxy(unsigned int idx, vtkSMProxy* proxy)
  {
    if (this->PPInternals->UncheckedProxies.size() <= idx)
      {
      this->PPInternals->UncheckedProxies.resize(idx+1);
      }
    this->PPInternals->UncheckedProxies[idx] = proxy;
  
    this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::RemoveAllUncheckedProxies()
  {
    this->PPInternals->UncheckedProxies.clear();
  
    this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::ClearUncheckedProxies()
  {
    this->PPInternals->UncheckedProxies.clear();
  
    for(unsigned int i = 0; i < this->PPInternals->Proxies.size(); i++)
      {
      this->PPInternals->UncheckedProxies.push_back(this->GetProxy(i));
      }
  
    this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
  }
  
  //---------------------------------------------------------------------------
  bool vtkSMProxyProperty::IsProxyAdded(vtkSMProxy* proxy)
  {
    vtkSMProxyPropertyInternals::VectorOfProxies::iterator iter =
      this->PPInternals->Proxies.begin();
    for ( ; iter != this->PPInternals->Proxies.end() ; ++iter)
      {
      if (iter->Value == proxy)
        {
        return true;
        }
      }
    return false;
  }
  
  //---------------------------------------------------------------------------
  int vtkSMProxyProperty::AddProxy(vtkSMProxy* proxy, int modify)
  {
    this->PPInternals->Proxies.push_back(vtkProxyPointer(this, proxy));
    if (modify)
      {
      this->Modified();
      }
  
    this->ClearUncheckedProxies();
    return 1;
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::RemoveProxy(vtkSMProxy* proxy)
  {
    this->RemoveProxy(proxy, 1);
  }
  
  //---------------------------------------------------------------------------
  unsigned int vtkSMProxyProperty::RemoveProxy(vtkSMProxy* proxy, int modify)
  {
    vtkSMProxyPropertyInternals::VectorOfProxies::iterator iter =
      this->PPInternals->Proxies.begin();
    unsigned int idx = 0;
    for ( ; iter != this->PPInternals->Proxies.end() ; ++iter, idx++)
      {
      if (iter->Value == proxy)
        {
        this->PPInternals->Proxies.erase(iter);
        if (modify)
          {
          this->Modified();
          }
        this->ClearUncheckedProxies();
        break;
        }
      }
    return idx;
  }
  
  //---------------------------------------------------------------------------
  int vtkSMProxyProperty::SetProxy(unsigned int idx, vtkSMProxy* proxy)
  {
    if (this->PPInternals->Proxies.size() > idx &&
        proxy == this->PPInternals->Proxies[idx].Value)
      {
      return 1;
      }
  
    if (this->PPInternals->Proxies.size() <= idx)
      {
      this->PPInternals->Proxies.resize(idx+1);
      }
  
    this->PPInternals->Proxies[idx] = vtkProxyPointer(this, proxy);
    this->Modified();
    this->ClearUncheckedProxies();
  
    return 1;
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::SetProxies(unsigned int numProxies,
    vtkSMProxy* proxies[])
  {
    this->PPInternals->Proxies.clear();
    for (unsigned int cc=0; cc < numProxies; cc++)
      {
      this->PPInternals->Proxies.push_back(vtkProxyPointer(this, proxies[cc]));
      }
  
    this->Modified();
    this->ClearUncheckedProxies();
  }
  
  //---------------------------------------------------------------------------
  int vtkSMProxyProperty::AddProxy(vtkSMProxy* proxy)
  {
    return this->AddProxy(proxy, 1);
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::RemoveAllProxies(int modify)
  {
    this->PPInternals->Proxies.clear();
    if (modify)
      {
      this->Modified();
      }
    this->ClearUncheckedProxies();
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::SetNumberOfProxies(unsigned int num)
  {
    if (num != 0)
      {
      this->PPInternals->Proxies.resize(num);
      }
    else
      {
      this->PPInternals->Proxies.clear();
      }
  
    this->ClearUncheckedProxies();
  }
  
  //---------------------------------------------------------------------------
  unsigned int vtkSMProxyProperty::GetNumberOfProxies()
  {
    return static_cast<unsigned int>(this->PPInternals->Proxies.size());
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::SetNumberOfUncheckedProxies(unsigned int num)
  {
    if (num > 0)
      {
      this->PPInternals->UncheckedProxies.resize(num);
      }
    else
      {
      this->PPInternals->UncheckedProxies.clear();
      }
  }
  
  //---------------------------------------------------------------------------
  unsigned int vtkSMProxyProperty::GetNumberOfUncheckedProxies()
  {
    return static_cast<unsigned int>(this->PPInternals->UncheckedProxies.size());
  }
  
  //---------------------------------------------------------------------------
  vtkSMProxy* vtkSMProxyProperty::GetProxy(unsigned int idx)
  {
    return this->PPInternals->Proxies[idx].Value.GetPointer();
  }
  
  //---------------------------------------------------------------------------
  vtkSMProxy* vtkSMProxyProperty::GetUncheckedProxy(unsigned int idx)
  {
    return this->PPInternals->UncheckedProxies[idx];
  }
  
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::WriteTo(vtkSMMessage* message)
  {
    ProxyState_Property *prop = message->AddExtension(ProxyState::property);
    prop->set_name(this->GetXMLName());
    Variant *var = prop->mutable_value();
    var->set_type(Variant::PROXY);
    for (unsigned int i=0; i<this->GetNumberOfProxies(); i++)
      {
      vtkSMProxy* argument = this->GetProxy(i);
      if (argument)
        {
        argument->CreateVTKObjects();
        var->add_proxy_global_id(argument->GetGlobalID());
        }
      else
        {
        var->add_proxy_global_id(0);
        }
      }
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::ReadFrom(const vtkSMMessage* message, int msg_offset,
                                    vtkSMProxyLocator* locator)
  {
    // FIXME this method is REALLY close to its vtkSMInputProperty subClass
    // Please keep them in sync
    const ProxyState_Property *prop = &message->GetExtension(ProxyState::property, msg_offset);
    if(strcmp(prop->name().c_str(), this->GetXMLName()) == 0)
      {
      const Variant *value = &prop->value();
      int nbProxies = value->proxy_global_id_size();
      std::set<vtkTypeUInt32> newProxyIdList;
      std::set<vtkTypeUInt32>::const_iterator proxyIdIter;
  
      // Fill indexed proxy id list
      for(int i=0; i < nbProxies; i++)
        {
        newProxyIdList.insert(value->proxy_global_id(i));
        }
  
      // Deal with existing proxy
      for(unsigned int i=0;i<this->GetNumberOfProxies();i++)
        {
        vtkSMProxy *proxy = this->GetProxy(i);
        vtkTypeUInt32 id = proxy->GetGlobalID();
        if((proxyIdIter=newProxyIdList.find(id)) == newProxyIdList.end())
          {
          // Not find => Need to be removed
          this->RemoveProxy(proxy, true);
          i--; // Make sure we don't skip a proxy in the analysis
          }
        else
          {
          // Already there, no need to add it twice
          newProxyIdList.erase(id);
          }
        }
  
      // Managed real new proxy
      for( proxyIdIter =  newProxyIdList.begin();
           proxyIdIter != newProxyIdList.end();
           proxyIdIter++ )
        {
        // Get the proxy from proxy manager
  
        vtkSMProxy* proxy;
        if(locator && vtkSMProxyProperty::CanCreateProxy())
          {
          proxy = locator->LocateProxy(*proxyIdIter);
          }
        else
          {
          proxy =
              vtkSMProxy::SafeDownCast(
                this->GetParent()->GetSession()->GetRemoteObject(*proxyIdIter));
          }
  
        if(proxy)
          {
          this->AddProxy(proxy, true);
          }
        // We shouldn't ReNew a Proxy if that Input was not found
        // else
        //   {
        //    // Recreate the proxy as it used to be
        //    proxy = pxm->ReNewProxy(*proxyIdIter, locator);
        //    if(proxy)
        //      {
        //       this->AddProxy(proxy, true);
        //       proxy->Delete();
        //      }
        //    }
        }
      }
    else
      {
      vtkWarningMacro("Invalid offset property");
      }
  }
  
  //---------------------------------------------------------------------------
  int vtkSMProxyProperty::ReadXMLAttributes(vtkSMProxy* parent,
                                            vtkPVXMLElement* element)
  {
    int skip_dependency;
    if (element->GetScalarAttribute("skip_dependency", &skip_dependency))
      {
      this->SkipDependency = (skip_dependency == 1);
      }
    return this->Superclass::ReadXMLAttributes(parent, element);
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::DeepCopy(vtkSMProperty* src, 
    const char* exceptionClass, int proxyPropertyCopyFlag)
  {
    vtkSMSessionProxyManager* pxm =
        vtkSMProxyManager::GetProxyManager()->GetSessionProxyManager(
            this->GetParent()->GetSession());
    vtkSMProxyProperty* dsrc = vtkSMProxyProperty::SafeDownCast(src);
  
    this->RemoveAllProxies();
    this->RemoveAllUncheckedProxies();
    
    if (dsrc)
      {
      int imUpdate = this->ImmediateUpdate;
      this->ImmediateUpdate = 0;
      unsigned int i;
      unsigned int numElems = dsrc->GetNumberOfProxies();
  
      for(i=0; i<numElems; i++)
        {
        vtkSMProxy* psrc = dsrc->GetProxy(i);
        vtkSMProxy* pdest = pxm->NewProxy(psrc->GetXMLGroup(), 
          psrc->GetXMLName());
        pdest->SetSession(psrc->GetSession());
        pdest->Copy(psrc, exceptionClass, proxyPropertyCopyFlag);
        this->AddProxy(pdest);
        pdest->Delete();
        }
      
      numElems = dsrc->GetNumberOfUncheckedProxies();
      for(i=0; i<numElems; i++)
        {
        vtkSMProxy* psrc = dsrc->GetUncheckedProxy(i);
        vtkSMProxy* pdest = pxm->NewProxy(psrc->GetXMLGroup(), 
          psrc->GetXMLName());
        pdest->SetSession(psrc->GetSession());
        pdest->Copy(psrc, exceptionClass, proxyPropertyCopyFlag);
        this->AddUncheckedProxy(pdest);
        pdest->Delete();
        }
      this->ImmediateUpdate = imUpdate;
      }
    if (this->ImmediateUpdate)
      {
      this->Modified();
      this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
      }
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::Copy(vtkSMProperty* src)
  {
    this->Superclass::Copy(src);
  
    bool prev = this->SetBlockModifiedEvents(true);
  
    this->RemoveAllProxies();
    this->RemoveAllUncheckedProxies();
  
    vtkSMProxyProperty* dsrc = vtkSMProxyProperty::SafeDownCast(
      src);
    if (dsrc)
      {
      int imUpdate = this->ImmediateUpdate;
      this->ImmediateUpdate = 0;
      unsigned int i;
      unsigned int numElems = dsrc->GetNumberOfProxies();
      for(i=0; i<numElems; i++)
        {
        this->AddProxy(dsrc->GetProxy(i));
        }
      numElems = dsrc->GetNumberOfUncheckedProxies();
      for(i=0; i<numElems; i++)
        {
        this->AddUncheckedProxy(dsrc->GetUncheckedProxy(i));
        }
      this->ImmediateUpdate = imUpdate;
      }
  
    this->SetBlockModifiedEvents(prev);
    this->Modified();
    this->InvokeEvent(vtkCommand::UncheckedPropertyModifiedEvent);
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::AddProducer(vtkSMProxy* producer)
  {
    if (producer && this->GetParent() && !this->SkipDependency)
      {
      this->PPInternals->ProducerCounts[producer]++;
      if (this->PPInternals->ProducerCounts[producer] == 1)
        {
        producer->AddConsumer(this, this->GetParent());
        this->GetParent()->AddProducer(this, producer);
        }
      }
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::RemoveProducer(vtkSMProxy* producer)
  {
    if (producer && this->GetParent() && !this->SkipDependency)
      {
      this->PPInternals->ProducerCounts[producer]--;
      assert(this->PPInternals->ProducerCounts[producer] >= 0);
      if (this->PPInternals->ProducerCounts[producer] == 0)
        {
        producer->RemoveConsumer(this, this->GetParent());
        this->GetParent()->RemoveProducer(this, producer);
        }
      }
  }
  
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::PrintSelf(ostream& os, vtkIndent indent)
  {
    this->Superclass::PrintSelf(os, indent);
    os << indent << "SkipDependency: "<< this->SkipDependency << endl;
    os << indent << "Values: ";
    for (unsigned int i=0; i<this->GetNumberOfProxies(); i++)
      {
      os << this->GetProxy(i) << " ";
      }
    os << endl;
  }
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::SaveStateValues(vtkPVXMLElement* propertyElement)
  {
    unsigned int size = this->GetNumberOfProxies();
    if (size > 0)
      {
      propertyElement->AddAttribute("number_of_elements", size);
      }
    for (unsigned int i=0; i<size; i++)
      {
      this->AddProxyElementState(propertyElement, i);
      }
  }
  //---------------------------------------------------------------------------
  vtkPVXMLElement* vtkSMProxyProperty::AddProxyElementState(vtkPVXMLElement *prop,
                                                            unsigned int idx)
  {
    vtkSMProxy* proxy = this->GetProxy(idx);
    vtkPVXMLElement* proxyElement = 0;
    if (proxy)
      {
      proxyElement = vtkPVXMLElement::New();
      proxyElement->SetName("Proxy");
      proxyElement->AddAttribute("value",
                                 static_cast<unsigned int>(proxy->GetGlobalID()));
      prop->AddNestedElement(proxyElement);
      proxyElement->FastDelete();
      }
    return proxyElement;
  }
  //---------------------------------------------------------------------------
  // NOTE: This method is duplicated in some way in vtkSMInputProperty
  // Therefore, care must be taken to keep the two in sync.
  int vtkSMProxyProperty::LoadState(vtkPVXMLElement* element,
                                    vtkSMProxyLocator* loader)
  {
    if (!loader)
      {
      // no loader specified, state remains unchanged.
      return 1;
      }
  
    int prevImUpdate = this->ImmediateUpdate;
  
    // Wait until all values are set before update (if ImmediateUpdate)
    this->ImmediateUpdate = 0;
    this->Superclass::LoadState(element, loader);
  
    // If "clear" is present and is 0, it implies that the proxy elements
    // currently in the property should not be cleared before loading
    // the new state.
    int clear=1;
    element->GetScalarAttribute("clear", &clear);
    if (clear)
      {
      this->PPInternals->Proxies.clear();
      }
  
    unsigned int numElems = element->GetNumberOfNestedElements();
    for (unsigned int i=0; i<numElems; i++)
      {
      vtkPVXMLElement* currentElement = element->GetNestedElement(i);
      if (currentElement->GetName() &&
          (strcmp(currentElement->GetName(), "Element") == 0 ||
           strcmp(currentElement->GetName(), "Proxy") == 0) )
        {
        int id;
        if (currentElement->GetScalarAttribute("value", &id))
          {
          if (id)
            {
            vtkSMProxy* proxy = loader->LocateProxy(id);
            if (proxy)
              {
              this->AddProxy(proxy, 0);
              }
            else
              {
              // It is not an error to have missing proxies in a proxy property.
              // We simply ignore such proxies.
              //vtkErrorMacro("Could not create proxy of id: " << id);
              //return 0;
              }
            }
          else
            {
            this->AddProxy(0, 0);
            }
          }
        }
      }
  
    // Do not immediately update. Leave it to the loader.
    this->Modified();
    this->ImmediateUpdate = prevImUpdate;
    return 1;
  }
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::EnableProxyCreation()
  {
    vtkSMProxyProperty::CreateProxyAllowed = true;
  }
  //---------------------------------------------------------------------------
  void vtkSMProxyProperty::DisableProxyCreation()
  {
    vtkSMProxyProperty::CreateProxyAllowed = false;
  }
  //---------------------------------------------------------------------------
  bool vtkSMProxyProperty::CanCreateProxy()
  {
    return vtkSMProxyProperty::CreateProxyAllowed;
  }
  