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

  Program:   ParaView
  Module:    vtkSIProxyDefinitionManager.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 "vtkSIProxyDefinitionManager.h"

#include "vtkCollection.h"
#include "vtkCollectionIterator.h"
#include "vtkCommand.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPVConfig.h"
#include "vtkPVPlugin.h"
#include "vtkPVPluginTracker.h"
#include "vtkPVProxyDefinitionIterator.h"
#include "vtkPVServerManagerPluginInterface.h"
#include "vtkPVSession.h"
#include "vtkPVXMLElement.h"
#include "vtkPVXMLParser.h"
#include "vtkProcessModule.h"
#include "vtkReservedRemoteObjectIds.h"
#include "vtkSMMessage.h"
#include "vtkSmartPointer.h"
#include "vtkStdString.h"
#include "vtkStringList.h"
#include "vtkTimerLog.h"

#include <sstream>
#include <map>
#include <vtksys/RegularExpression.hxx>
#include <set>
#include <string>
#include <vector>

#include <assert.h>

// this file must be included after vtkPVConfig etc. are included.
// #include "vtkSMGeneratedModules.h"

//****************************************************************************/
//                    Internal Classes and typedefs
//****************************************************************************/
typedef vtkSmartPointer<vtkPVXMLElement>        XMLElement;
typedef std::map<vtkStdString, XMLElement>   StrToXmlMap;
typedef std::map<vtkStdString, StrToXmlMap>  StrToStrToXmlMap;

class vtkSIProxyDefinitionManager::vtkInternals
{
public:
  // Keep State Flag of the ProcessType
  bool EnableXMLProxyDefinitionUpdate;
  // Keep track of ServerManager definition
  StrToStrToXmlMap CoreDefinitions;
  // Keep track of custom definition
  StrToStrToXmlMap CustomsDefinitions;
  //-------------------------------------------------------------------------
  vtkInternals() : EnableXMLProxyDefinitionUpdate(true) {}
  //-------------------------------------------------------------------------
  void Clear()
    {
    this->CoreDefinitions.clear();
    this->CustomsDefinitions.clear();
    }
  //-------------------------------------------------------------------------
  bool HasCoreDefinition( const char* groupName, const char* proxyName)
  {
    return this->GetProxyElement( this->CoreDefinitions,
                                  groupName, proxyName) != NULL;
  }
  //-------------------------------------------------------------------------
  bool HasCustomDefinition( const char* groupName, const char* proxyName)
  {
    return this->GetProxyElement( this->CustomsDefinitions,
                                  groupName, proxyName ) != NULL;
  }
  //-------------------------------------------------------------------------
  unsigned int GetNumberOfProxy(const char* groupName)
  {
    unsigned int nbProxy = 0;
    if(groupName)
    {
      nbProxy += static_cast<unsigned int>(
        this->CoreDefinitions[groupName].size());
      nbProxy += static_cast<unsigned int>(
        this->CustomsDefinitions[groupName].size());
    }
    return nbProxy;
  }
  //-------------------------------------------------------------------------
  vtkPVXMLElement* GetProxyElement( const char* groupName,
                                    const char* proxyName )
  {
    vtkPVXMLElement* elementToReturn = NULL;

    // Search in ServerManager definitions
    elementToReturn = this->GetProxyElement( this->CoreDefinitions,
                                             groupName, proxyName );

    // If not found yet, search in customs ones...
    if(elementToReturn == NULL)
      {
      elementToReturn = this->GetProxyElement( this->CustomsDefinitions,
                                         groupName, proxyName );
      }

    return elementToReturn;
  }
  //-------------------------------------------------------------------------
  vtkPVXMLElement* GetProxyElement( const StrToStrToXmlMap& map,
                                    const char* firstStr,
                                    const char* secondStr)
  {
    vtkPVXMLElement* elementToReturn = NULL;

    // Test if parameters are valid
    if (firstStr && secondStr)
      {
      // Find the value based on both keys
      StrToStrToXmlMap::const_iterator it = map.find(firstStr);
      if (it != map.end())
        {
        // We found a match for the first key
        StrToXmlMap::const_iterator it2 = it->second.find(secondStr);
        if (it2 != it->second.end())
          {
          // We found a match for the second key
          elementToReturn = it2->second.GetPointer();
          }
        }
      }

    // The result might be NULL if the value was not found
    return elementToReturn;
  }

  static void ExtractMetaInformation(vtkPVXMLElement* proxy,
                                     std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > &subProxyMap,
                                     std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > &propertyMap)
    {
    std::set<std::string> propertyTypeName;
    propertyTypeName.insert("DoubleVectorProperty");
    propertyTypeName.insert("IntVectorProperty");
    propertyTypeName.insert("ProxyProperty");
    propertyTypeName.insert("Property");

    unsigned int numChildren = proxy->GetNumberOfNestedElements();
    unsigned int cc;
    for (cc=0; cc < numChildren; cc++)
      {
      vtkPVXMLElement* child = proxy->GetNestedElement(cc);
      if (child && child->GetName())
        {
        if(strcmp(child->GetName(), "SubProxy") == 0)
          { // SubProxy
          if(child->GetAttribute("name"))
            {
            subProxyMap[child->GetAttribute("name")] = child;
            }
          vtkPVXMLElement* exposedPropElement =
              child->FindNestedElementByName("ExposedProperties");

          // exposedPropElement can be NULL if only Shared property are used...
          if(exposedPropElement)
            {
            unsigned int ccSub = 0;
            unsigned int numChildrenSub = exposedPropElement->GetNumberOfNestedElements();
            for(ccSub = 0; ccSub < numChildrenSub; ccSub++)
              {
              vtkPVXMLElement* subProxyChild = exposedPropElement->GetNestedElement(ccSub);
              if (subProxyChild && subProxyChild->GetName()
                && propertyTypeName.find(subProxyChild->GetName()) != propertyTypeName.end())
                  { // Property
                const char* propName = subProxyChild->GetAttribute("exposed_name");
                propName = propName ? propName : subProxyChild->GetAttribute("name");
                propertyMap[propName] = subProxyChild;
                } // Property end ---------------------
              }
            }
          } // SubProxy end ------------------
        else if(propertyTypeName.find(child->GetName()) != propertyTypeName.end())
          { // Property
          const char* propName = child->GetAttribute("exposed_name");
          propName = propName ? propName : child->GetAttribute("name");
          propertyMap[propName] = child;
          } // Property end ---------------------
        }
      }
    }

};
//****************************************************************************/
class vtkInternalDefinitionIterator : public vtkPVProxyDefinitionIterator
{
public:

  static vtkInternalDefinitionIterator* New();
  vtkTypeMacro(vtkInternalDefinitionIterator, vtkPVProxyDefinitionIterator);

  //-------------------------------------------------------------------------
  void GoToFirstItem()
  {
    this->Reset();
  }
  //-------------------------------------------------------------------------
  void GoToNextItem()
  {
    if(!this->IsDoneWithCoreTraversal())
      {
      this->NextCoreDefinition();
      }
    else if(!this->IsDoneWithCustomTraversal())
      {
      this->NextCustomDefinition();
      }
    else
      {
      while( this->IsDoneWithCoreTraversal() &&
             this->IsDoneWithCustomTraversal() &&
             !this->IsDoneWithGroupTraversal() )
        {
        this->NextGroup();
        }
      }
  }
  //-------------------------------------------------------------------------
  bool IsDoneWithTraversal()
  {
    if(!this->Initialized)
    {
      this->GoToFirstItem();
    }
    if( this->IsDoneWithCoreTraversal() && this->IsDoneWithCustomTraversal())
      {
      if(this->IsDoneWithGroupTraversal())
        {
        return true;
        }
      else
        {
        this->NextGroup();
        return this->IsDoneWithTraversal();
        }
      }
    return false;
  }
  //-------------------------------------------------------------------------
  virtual const char* GetGroupName()
  {
    return this->CurrentGroupName.c_str();
  }
  //-------------------------------------------------------------------------
  virtual const char* GetProxyName()
  {
    if(this->IsCustom())
      {
      return this->CustomProxyIterator->first.c_str();
      }
    else
      {
      return this->CoreProxyIterator->first.c_str();
      }
  }
  //-------------------------------------------------------------------------
  virtual bool IsCustom()
  {
    return this->IsDoneWithCoreTraversal();
  }
  //-------------------------------------------------------------------------
  virtual vtkPVXMLElement* GetProxyDefinition()
  {
    if(this->IsCustom())
      {
      return this->CustomProxyIterator->second.GetPointer();
      }
    else
      {
      return this->CoreProxyIterator->second.GetPointer();
      }
  }
  //-------------------------------------------------------------------------
  virtual vtkPVXMLElement* GetProxyHints()
  {
    vtkPVXMLElement* definition = this->GetProxyDefinition();
    if(definition)
      {
      return definition->FindNestedElementByName("Hints");
      }
    return NULL;
  }
  //-------------------------------------------------------------------------
  void AddTraversalGroupName(const char* groupName)
  {
    this->GroupNames.insert(vtkStdString(groupName));
  }
  //-------------------------------------------------------------------------
  void RegisterCoreDefinitionMap(StrToStrToXmlMap* map)
  {
    this->CoreDefinitionMap = map;
    this->InvalidCoreIterator = true;
  }
  //-------------------------------------------------------------------------
  void RegisterCustomDefinitionMap(StrToStrToXmlMap* map)
  {
    this->CustomDefinitionMap = map;
    this->InvalidCustomIterator = true;
  }

 //-------------------------------------------------------------------------
  void GoToNextGroup()
  {
    this->NextGroup();
  }

protected:
  vtkInternalDefinitionIterator()
  {
    this->Initialized = false;
    this->CoreDefinitionMap = NULL;
    this->CustomDefinitionMap = 0;
    this->InvalidCoreIterator = true;
    this->InvalidCustomIterator = true;
  }
  ~vtkInternalDefinitionIterator(){}

  //-------------------------------------------------------------------------
  void Reset()
  {
    this->Initialized = true;
    this->InvalidCoreIterator = true;
    this->InvalidCustomIterator = true;

    if(this->GroupNames.size() == 0)
      {
      // Look for all name available
      if(this->CoreDefinitionMap)
        {
        StrToStrToXmlMap::iterator it = this->CoreDefinitionMap->begin();
        while(it != this->CoreDefinitionMap->end())
          {
          this->AddTraversalGroupName(it->first.c_str());
          it++;
          }
        }
      if(this->CustomDefinitionMap)
        {
        StrToStrToXmlMap::iterator it = this->CustomDefinitionMap->begin();
        while(it != this->CustomDefinitionMap->end())
          {
          this->AddTraversalGroupName(it->first.c_str());
          it++;
          }
        }

      if(this->GroupNames.size() == 0)
        {
        // TODO vtkErrorMacro("No definition available for that iterator.");
        return;
        }
      this->Initialized = false;
      this->Reset();
      }
    else
      {
      this->GroupNameIterator = this->GroupNames.begin();
      }
  }
  //-------------------------------------------------------------------------
  bool IsDoneWithGroupTraversal()
  {
    return this->GroupNames.size() == 0
        || this->GroupNameIterator == this->GroupNames.end();
  }
  //-------------------------------------------------------------------------
  bool IsDoneWithCoreTraversal()
  {
    if(this->CoreDefinitionMap && !this->InvalidCoreIterator)
      {
      return this->CoreProxyIterator == this->CoreProxyIteratorEnd;
      }
    return true;
  }
  //-------------------------------------------------------------------------
  bool IsDoneWithCustomTraversal()
  {
    if(this->CustomDefinitionMap && !this->InvalidCustomIterator)
      {
      return this->CustomProxyIterator == this->CustomProxyIteratorEnd;
      }
    return true;
  }
  //-------------------------------------------------------------------------
  void NextGroup()
  {
    this->CurrentGroupName = *GroupNameIterator;
    this->GroupNameIterator++;
    if(this->CoreDefinitionMap)
      {
      this->CoreProxyIterator    =
          (*this->CoreDefinitionMap)[this->CurrentGroupName].begin();
      this->CoreProxyIteratorEnd =
          (*this->CoreDefinitionMap)[this->CurrentGroupName].end();
      this->InvalidCoreIterator = false;
      }
    if(this->CustomDefinitionMap)
      {
      this->CustomProxyIterator    =
          (*this->CustomDefinitionMap)[this->CurrentGroupName].begin();
      this->CustomProxyIteratorEnd =
          (*this->CustomDefinitionMap)[this->CurrentGroupName].end();
      this->InvalidCustomIterator = false;
      }
  }
  //-------------------------------------------------------------------------
  void NextCoreDefinition()
  {
    this->CoreProxyIterator++;
  }
  //-------------------------------------------------------------------------
  void NextCustomDefinition()
  {
    this->CustomProxyIterator++;
  }

private:
  bool Initialized;
  vtkStdString CurrentGroupName;
  StrToXmlMap::iterator CoreProxyIterator;
  StrToXmlMap::iterator CoreProxyIteratorEnd;
  StrToXmlMap::iterator CustomProxyIterator;
  StrToXmlMap::iterator CustomProxyIteratorEnd;
  StrToStrToXmlMap* CoreDefinitionMap;
  StrToStrToXmlMap* CustomDefinitionMap;
  std::set<vtkStdString> GroupNames;
  std::set<vtkStdString>::iterator GroupNameIterator;
  bool InvalidCoreIterator;
  bool InvalidCustomIterator;
};

//****************************************************************************/
vtkStandardNewMacro(vtkSIProxyDefinitionManager)
vtkStandardNewMacro(vtkInternalDefinitionIterator)
//---------------------------------------------------------------------------
vtkSIProxyDefinitionManager::vtkSIProxyDefinitionManager()
{
  this->Internals = new vtkInternals;
  this->InternalsFlatten = new vtkInternals;

  vtkPVPluginTracker* tracker = vtkPVPluginTracker::GetInstance();

  // Load the core xmls.
  // These are loaded from the vtkPVInitializerPlugin plugin.
  for (unsigned int cc=0; cc < tracker->GetNumberOfPlugins(); cc++)
    {
    vtkPVPlugin* plugin = tracker->GetPlugin(cc);
    if (plugin && strcmp(plugin->GetPluginName(), "vtkPVInitializerPlugin") == 0)
      {
      this->HandlePlugin(plugin);
      break;
      }
    }

  // Now, process any other loaded plugins. This has to happen after loading the
  // core xmls (BUG #13488).
  for (unsigned int cc=0; cc < tracker->GetNumberOfPlugins(); cc++)
    {
    vtkPVPlugin* plugin = tracker->GetPlugin(cc);
    if (plugin && strcmp(plugin->GetPluginName(), "vtkPVInitializerPlugin") == 0)
      {
      continue;
      }

    this->HandlePlugin(plugin);
    }

  // Register with the plugin tracker, so that when new plugins are loaded,
  // we parse the XML if provided and automatically add it to the proxy
  // definitions.
  tracker->AddObserver( vtkCommand::RegisterEvent, this,
                        &vtkSIProxyDefinitionManager::OnPluginLoaded);

}

//---------------------------------------------------------------------------
vtkSIProxyDefinitionManager::~vtkSIProxyDefinitionManager()
{
  delete this->Internals;
  delete this->InternalsFlatten;
}

//----------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::InvokeCustomDefitionsUpdated()
{
  this->InvokeEvent(
    vtkSIProxyDefinitionManager::CompoundProxyDefinitionsUpdated);
  this->InvokeEvent(
    vtkSIProxyDefinitionManager::ProxyDefinitionsUpdated);
}

//----------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::AddElement(const char* groupName,
                                             const char* proxyName,
                                             vtkPVXMLElement* element)
{
  bool updated = false;
  if (element->GetName() && strcmp(element->GetName(), "Extension") == 0)
    {
    // This is an extension for an existing definition.
    vtkPVXMLElement* coreElem = this->Internals->GetProxyElement(
        this->Internals->CoreDefinitions, groupName, proxyName);
    if(coreElem)
      {
      // We found it, so we can extend it
      for (unsigned int cc=0; cc < element->GetNumberOfNestedElements(); cc++)
        {
        coreElem->AddNestedElement(element->GetNestedElement(cc));
        }
      updated = true;
      }
    else
      {
      vtkWarningMacro("Extension for (" << groupName << ", " << proxyName
                      << ") ignored since could not find core definition.");
      }
    }
  else
    {
    // Just referenced it
    this->Internals->CoreDefinitions[groupName][proxyName] = element;
    updated = true;
    }

  if (updated)
    {
    // Let the world know that a core-definition was registered i.e. added or
    // modified.
    RegisteredDefinitionInformation info(groupName, proxyName, false);
    this->InvokeEvent(vtkCommand::RegisterEvent, &info);
    }
}

//---------------------------------------------------------------------------
vtkPVXMLElement* vtkSIProxyDefinitionManager::GetProxyDefinition(
                 const char* groupName, const char* proxyName,
                 const bool throwError)
{
  vtkPVXMLElement* element = this->Internals->GetProxyElement( groupName,
                                                               proxyName );
  if (!throwError || element)
    {
    return element;
    }
  else
    {
    vtkErrorMacro( << "No proxy that matches: group=" << groupName
                   << " and proxy=" << proxyName << " were found.");
    }
  return 0;
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::ClearCustomProxyDefinitions()
{
  this->Internals->CustomsDefinitions.clear();
  this->InvokeCustomDefitionsUpdated();
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::RemoveCustomProxyDefinition(
    const char* groupName, const char* proxyName)
{
  if (this->Internals->HasCustomDefinition(groupName, proxyName))
    {
    this->Internals->CustomsDefinitions[groupName].erase(proxyName);

    // Let the world know that definitions may have changed.
    RegisteredDefinitionInformation info(groupName, proxyName, true);
    this->InvokeEvent(vtkCommand::UnRegisterEvent, &info);
    this->InvokeCustomDefitionsUpdated();
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::AttachShowInMenuHintsToProxy(vtkPVXMLElement* proxy)
{
  if(!proxy)
    {
    return;
    }

  vtkPVXMLElement* hints = proxy->FindNestedElementByName("Hints");
  if(hints == NULL)
    {
    vtkNew<vtkPVXMLElement> madehints;
    madehints->SetName("Hints");
    vtkNew<vtkPVXMLElement> showInMenu;
    showInMenu->SetName("ShowInMenu");
    madehints->AddNestedElement(showInMenu.GetPointer());
    proxy->AddNestedElement(madehints.GetPointer());
    }
  else if(hints->FindNestedElementByName("ShowInMenu") == NULL)
    {
    vtkNew<vtkPVXMLElement> showInMenu;
    showInMenu->SetName("ShowInMenu");
    hints->AddNestedElement(showInMenu.GetPointer());
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::
    AttachShowInMenuHintsToProxyFromProxyGroups(vtkPVXMLElement* root)
{
  if(!root)
    {
    return;
    }

  if(!strcmp(root->GetName(), "ProxyGroup"))
    {
    if( !strcmp(root->GetAttribute("name"), "sources") ||
        !strcmp(root->GetAttribute("name"), "filters"))
      {
      int size = root->GetNumberOfNestedElements();
      for(int cc=0; cc < size; ++cc)
        {
        this->AttachShowInMenuHintsToProxy(root->GetNestedElement(cc));
        }
      }
    }
  else
    {
    vtkNew<vtkCollection> collection;
    root->FindNestedElementByName("ProxyGroup", collection.GetPointer());
    int size = collection->GetNumberOfItems();
    for(int cc=0; cc < size; ++cc)
      {
      vtkPVXMLElement* group =
          vtkPVXMLElement::SafeDownCast(collection->GetItemAsObject(cc));
      this->AttachShowInMenuHintsToProxyFromProxyGroups(group);
      }
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::AddCustomProxyDefinition(
    const char* groupName, const char* proxyName, const char* xmlcontents)
{
  vtkNew<vtkPVXMLParser> parser;
  if (parser->Parse(xmlcontents))
    {
    this->AddCustomProxyDefinition(groupName, proxyName,
      parser->GetRootElement());
    }
  else
    {
    vtkErrorMacro("Failed to parse xml.");
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::AddCustomProxyDefinition(
    const char* groupName, const char* proxyName, vtkPVXMLElement* top)
{
  if (this->AddCustomProxyDefinitionInternal(groupName, proxyName, top))
    {
    this->InvokeCustomDefitionsUpdated();
    }
}

//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::AddCustomProxyDefinitionInternal(
    const char* groupName, const char* proxyName, vtkPVXMLElement* top)
{
  if (!top)
    {
    return false;
    }

  // Attach automatic hints so it will show up in the menu
  if(!strcmp(groupName, "sources") || !strcmp(groupName, "filters"))
    {
    this->AttachShowInMenuHintsToProxy(top);
    }

  vtkPVXMLElement* currentCustomElement =
      this->Internals->GetProxyElement( this->Internals->CustomsDefinitions,
                                        groupName, proxyName);

  // There's a possibility that the custom proxy definition is the
  // state has already been loaded (or another proxy definition with
  // the same name exists). If that existing definition matches what
  // the state is requesting, we don't need to raise any errors,
  // simply skip it.
  if(currentCustomElement && !currentCustomElement->Equals(top))
    {
    // A definition already exist with not the same content
    vtkErrorMacro("Proxy definition has already been registered with name \""
                  << proxyName << "\" under group \"" << groupName <<"\".");
    return false;
    }
  else
    {
    this->Internals->CustomsDefinitions[groupName][proxyName] = top;

    // Let the world know that definitions may have changed.
    RegisteredDefinitionInformation info(groupName, proxyName, true);
    this->InvokeEvent(vtkCommand::RegisterEvent, &info);
    return true;
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::LoadCustomProxyDefinitions(vtkPVXMLElement* root)
{
  if (!root)
    {
    return;
    }

  bool updated = true;
  vtksys::RegularExpression proxyDefRe(".*Proxy$");
  unsigned int numElems = root->GetNumberOfNestedElements();
  for (unsigned int i=0; i<numElems; i++)
    {
    vtkPVXMLElement* currentElement = root->GetNestedElement(i);
    if ( currentElement->GetName() &&
         strcmp(currentElement->GetName(), "CustomProxyDefinition") == 0 )
      {
      std::string group = currentElement->GetAttributeOrEmpty("group");
      std::string name = currentElement->GetAttributeOrEmpty("name");
      if (!name.empty() && !group.empty())
        {
        if (currentElement->GetNumberOfNestedElements() == 1)
          {
          vtkPVXMLElement* defElement = currentElement->GetNestedElement(0);
          const char* tagName = defElement->GetName();
          if (tagName && proxyDefRe.find(tagName))
            {
            // Register custom proxy definitions for all elements ending with
            // "Proxy".
            updated = this->AddCustomProxyDefinitionInternal(group.c_str(),
              name.c_str(), defElement) || updated;
            }
          }
        }
      else
        {
        vtkErrorMacro("Missing name or group");
        }
      }
    }
  if (updated)
    {
    this->InvokeCustomDefitionsUpdated();
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::LoadCustomProxyDefinitionsFromString(
  const char* xmlContent)
{
  vtkNew<vtkPVXMLParser> parser;
  if (parser->Parse(xmlContent))
    {
    this->LoadCustomProxyDefinitions(parser->GetRootElement());
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::SaveCustomProxyDefinitions(
  vtkPVXMLElement* root)
{
  assert(root != NULL);

  vtkPVProxyDefinitionIterator* iter =
      this->NewIterator(vtkSIProxyDefinitionManager::CUSTOM_DEFINITIONS);
  while(!iter->IsDoneWithTraversal())
    {
    vtkPVXMLElement* elem = iter->GetProxyDefinition();
    if (elem)
      {
      vtkNew<vtkPVXMLElement> defElement;
      defElement->SetName("CustomProxyDefinition");
      defElement->AddAttribute("name", iter->GetProxyName());
      defElement->AddAttribute("group", iter->GetGroupName());
      defElement->AddNestedElement(elem, 0);
      root->AddNestedElement(defElement.GetPointer());
      }
    iter->GoToNextItem();
    }
  iter->Delete();
}

//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::LoadConfigurationXMLFromString(const char* xmlContent)
{
  return this->LoadConfigurationXMLFromString(xmlContent, false);
}
//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::LoadConfigurationXMLFromString( const char* xmlContent,
                                                                  bool attachHints)
{
  vtkNew<vtkPVXMLParser> parser;
  return (parser->Parse(xmlContent) != 0) &&
      this->LoadConfigurationXML( parser->GetRootElement(), attachHints );
}

//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::LoadConfigurationXML(vtkPVXMLElement* root)
{
  return this->LoadConfigurationXML(root, false);
}

//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::LoadConfigurationXML(vtkPVXMLElement* root, bool attachHints)
{
  if (!root)
    {
    return false;
    }

  if (!root->GetName() ||
    strcmp(root->GetName(), "ServerManagerConfiguration") != 0)
    {
    // find nested ServerManagerConfiguration element and process that.
    return this->LoadConfigurationXML(
      root->FindNestedElementByName("ServerManagerConfiguration"),
      attachHints);
    }

  // Attach ShowInMenu hints
  if(attachHints)
    {
    this->AttachShowInMenuHintsToProxyFromProxyGroups(root);
    }

  // Loop over the top-level elements.
  for (unsigned int i=0; i < root->GetNumberOfNestedElements(); ++i)
    {
    vtkPVXMLElement* group = root->GetNestedElement(i);
    std::string groupName = group->GetAttributeOrEmpty("name");
    std::string proxyName;

    // Loop over the top-level elements.
    for(unsigned int cc=0; cc < group->GetNumberOfNestedElements(); ++cc)
      {
      vtkPVXMLElement* proxy = group->GetNestedElement(cc);
      proxyName = proxy->GetAttributeOrEmpty("name");
      if (!proxyName.empty())
        {
        this->AddElement(groupName.c_str(), proxyName.c_str(), proxy);
        }
      }
    }
  this->InvokeEvent(vtkSIProxyDefinitionManager::ProxyDefinitionsUpdated);
  return true;
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
//---------------------------------------------------------------------------
// vtkSIProxyDefinitionManager::ALL_DEFINITIONS    = 0
// vtkSIProxyDefinitionManager::CORE_DEFINITIONS   = 1
// vtkSIProxyDefinitionManager::CUSTOM_DEFINITIONS = 2
vtkPVProxyDefinitionIterator*
    vtkSIProxyDefinitionManager::NewSingleGroupIterator(char const* groupName,
                                                        int scope)
{
  vtkPVProxyDefinitionIterator* iterator = this->NewIterator(scope);
  iterator->AddTraversalGroupName(groupName);
  return iterator;
}
//---------------------------------------------------------------------------
// vtkSIProxyDefinitionManager::ALL_DEFINITIONS    = 0
// vtkSIProxyDefinitionManager::CORE_DEFINITIONS   = 1
// vtkSIProxyDefinitionManager::CUSTOM_DEFINITIONS = 2
vtkPVProxyDefinitionIterator* vtkSIProxyDefinitionManager::NewIterator(int scope)
{
  vtkInternalDefinitionIterator* iterator = vtkInternalDefinitionIterator::New();
  switch(scope)
    {
    case vtkSIProxyDefinitionManager::CORE_DEFINITIONS: // Core only
      iterator->RegisterCoreDefinitionMap( & this->Internals->CoreDefinitions);
      break;
    case vtkSIProxyDefinitionManager::CUSTOM_DEFINITIONS: // Custom only
      iterator->RegisterCustomDefinitionMap( & this->Internals->CustomsDefinitions);
      break;
    default: // Both
      iterator->RegisterCoreDefinitionMap( & this->Internals->CoreDefinitions);
      iterator->RegisterCustomDefinitionMap( & this->Internals->CustomsDefinitions);
      break;
    }
  return iterator;
}
//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::InvalidateCollapsedDefinition()
{
  this->InternalsFlatten->CoreDefinitions.clear();
}
//---------------------------------------------------------------------------
vtkPVXMLElement* vtkSIProxyDefinitionManager::ExtractSubProxy(
    vtkPVXMLElement* proxyDefinition, const char* subProxyName)
{
  if(!subProxyName)
    {
    return proxyDefinition;
    }

  vtksys::RegularExpression proxyDefRe(".*Proxy$");

  // Extract just the sub-proxy in-line definition
  for(unsigned int cc=0;cc<proxyDefinition->GetNumberOfNestedElements();cc++)
    {
    if(!strcmp(proxyDefinition->GetNestedElement(cc)->GetName(), "SubProxy"))
      {
      unsigned int nbChildren =
          proxyDefinition->GetNestedElement(cc)->GetNumberOfNestedElements();
      for(unsigned int childIdx = 0; childIdx < nbChildren; childIdx++)
        {
        vtkPVXMLElement* subProxyDef =
            proxyDefinition->GetNestedElement(cc)->GetNestedElement(childIdx);
        // Look for element name that are ending with "Proxy"
        const char* tagname = subProxyDef->GetName();
        if ( tagname && proxyDefRe.find(tagname) &&
             !strcmp(subProxyDef->GetAttribute("name"), subProxyName) )
          {
          return subProxyDef;
          }
        }
      }
    }

  return NULL;
}

//---------------------------------------------------------------------------
vtkPVXMLElement* vtkSIProxyDefinitionManager::GetCollapsedProxyDefinition(
    const char* group, const char* name, const char* subProxyName, bool throwError)
{
  // Look in the cache
  vtkPVXMLElement* flattenDefinition =
      this->InternalsFlatten->GetProxyElement(group,name);
  if (flattenDefinition)
    {
    // Found it, so return it...
    return this->ExtractSubProxy(flattenDefinition, subProxyName);
    }

  // Not found in the cache, look if the definition exists
  vtkPVXMLElement* originalDefinition =
      this->GetProxyDefinition(group, name, throwError);

  // Look for parent hierarchy
  if(originalDefinition)
    {
    vtkPVXMLElement* realDefinition = originalDefinition;
    std::string base_group =
        originalDefinition->GetAttributeOrEmpty("base_proxygroup");
    std::string base_name =
        originalDefinition->GetAttributeOrEmpty("base_proxyname");

    if( !base_group.empty() && !base_name.empty())
      {
      std::vector<vtkPVXMLElement*> classHierarchy;
      while(originalDefinition)
        {
        classHierarchy.push_back(originalDefinition);
        if(!base_group.empty() && !base_name.empty())
          {
          originalDefinition = this->GetProxyDefinition( base_group.c_str(),
                                                         base_name.c_str(),
                                                         throwError);
          if (!originalDefinition)
            {
            vtkErrorMacro("Failed to locate base proxy definition ("
              << base_group.c_str() << ", " << base_name.c_str()
              << "). Aborting for debugging purposes.");
            abort();
            }
          base_group =
              originalDefinition->GetAttributeOrEmpty("base_proxygroup");
          base_name  =
              originalDefinition->GetAttributeOrEmpty("base_proxyname");
          }
        else
          {
          originalDefinition = 0;
          }
        }

      // Build the flattened version of it
      vtkNew<vtkPVXMLElement> newElement;
      while(classHierarchy.size() > 0)
        {
        vtkPVXMLElement* currentElement = classHierarchy.back();
        classHierarchy.pop_back();
        this->MergeProxyDefinition(currentElement, newElement.GetPointer());
        }
      realDefinition->CopyAttributesTo(newElement.GetPointer());

      // Remove parent declaration
      newElement->RemoveAttribute("base_proxygroup");
      newElement->RemoveAttribute("base_proxyname");

      // Register it in the cache
      this->InternalsFlatten->CoreDefinitions[group][name] = newElement.GetPointer();

      return this->ExtractSubProxy(newElement.GetPointer(), subProxyName);
      }
    }

  // Could be either the original definition or a NULL pointer if not found
  return this->ExtractSubProxy(originalDefinition, subProxyName);
}
//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::MergeProxyDefinition(vtkPVXMLElement* element,
                                                       vtkPVXMLElement* elementToFill)
{
  // Meta-data of elementToFill
  std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > subProxyToFill;
  std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > propertiesToFill;
  vtkInternals::ExtractMetaInformation( elementToFill,
                                        subProxyToFill,
                                        propertiesToFill);

  // Meta-data of element that should be merged into the other
  std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > subProxySrc;
  std::map<std::string, vtkSmartPointer<vtkPVXMLElement> > propertiesSrc;
  vtkInternals::ExtractMetaInformation( element,
                                        subProxySrc,
                                        propertiesSrc);

  // Look for conflicting sub-proxy name and remove their definition if override
  std::map<std::string, vtkSmartPointer<vtkPVXMLElement> >::iterator mapIter;
  mapIter = subProxyToFill.begin();
  while( mapIter != subProxyToFill.end())
    {
    std::string name = mapIter->first;
    if( subProxySrc.find(name) != subProxySrc.end() )
      {
      if (!subProxySrc[name]->GetAttribute("override"))
        {
        vtkWarningMacro("#####################################" << endl
                        << "Find conflict between 2 SubProxy name. ("
                        << name.c_str() << ")" << endl
                        << "#####################################" << endl);
        return;
        }
      else
        { // Remove the given subProxy of the Element to Fill
        vtkPVXMLElement *subProxyDefToRemove = subProxyToFill[name].GetPointer();
        subProxyDefToRemove->GetParent()->RemoveNestedElement(subProxyDefToRemove);
        }
      }
    // Move to next
    mapIter++;
    }

  // Look for conflicting property name and remove their definition if override
  mapIter = propertiesToFill.begin();
  while( mapIter != propertiesToFill.end())
    {
    std::string name = mapIter->first;
    if( propertiesSrc.find(name) != propertiesSrc.end())
      {
      if (!propertiesSrc[name]->GetAttribute("override") &&
          !propertiesSrc[name]->GetParent()->GetParent()->FindNestedElementByName("Proxy")->GetAttribute("override"))
        {
        vtkWarningMacro(<< "Find conflict between 2 property name. ("
                        << name.c_str() << ")");
        return;
        }
      else
        { // Remove the given property of the Element to Fill
        vtkPVXMLElement *subPropDefToRemove = propertiesToFill[name].GetPointer();
        subPropDefToRemove->GetParent()->RemoveNestedElement(subPropDefToRemove);
        }
      }
    // Move to next
    mapIter++;
    }

  // By default alway overide the documentation
  if( element->FindNestedElementByName("Documentation") &&
      elementToFill->FindNestedElementByName("Documentation"))
    {
    elementToFill->RemoveNestedElement(
        elementToFill->FindNestedElementByName("Documentation"));
    }

  // Fill the output with all the input elements
  unsigned int numChildren = element->GetNumberOfNestedElements();
  unsigned int cc;
  for (cc=0; cc < numChildren; cc++)
    {
    vtkPVXMLElement* child = element->GetNestedElement(cc);
    vtkNew<vtkPVXMLElement> newElement;
    child->CopyTo(newElement.GetPointer());
    elementToFill->AddNestedElement(newElement.GetPointer());
    }
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::Pull(vtkSMMessage* msg)
{
  // Setup required message header
  msg->Clear();
  msg->set_global_id(vtkSIProxyDefinitionManager::GetReservedGlobalID());
  msg->set_location(vtkPVSession::DATA_SERVER);

  // This is made in a naive way, but we are sure that at each request
  // we have the correct and latest definition available.
  // This is not the most efficient way to do it. But optimistation should come
  // after. And for now, it is the less intrusive way to deal with server
  // XML definition centralisation state.
  ProxyDefinitionState_ProxyXMLDefinition *xmlDef;
  vtkPVProxyDefinitionIterator* iter;

  // Core Definition
  iter = this->NewIterator(vtkSIProxyDefinitionManager::CORE_DEFINITIONS);
  iter->GoToFirstItem();
  while( !iter->IsDoneWithTraversal() )
    {
    std::ostringstream xmlContent;
    iter->GetProxyDefinition()->PrintXML(xmlContent, vtkIndent());

    xmlDef = msg->AddExtension(ProxyDefinitionState::xml_definition_proxy);
    xmlDef->set_group(iter->GetGroupName());
    xmlDef->set_name(iter->GetProxyName());
    xmlDef->set_xml(xmlContent.str());

    iter->GoToNextItem();
    }
  iter->Delete();

  // Custome Definition
  iter = this->NewIterator(vtkSIProxyDefinitionManager::CUSTOM_DEFINITIONS);
  iter->GoToFirstItem();
  while( !iter->IsDoneWithTraversal() )
    {
    std::ostringstream xmlContent;
    iter->GetProxyDefinition()->PrintXML(xmlContent, vtkIndent());

    xmlDef = msg->AddExtension(ProxyDefinitionState::xml_custom_definition_proxy);
    xmlDef->set_group(iter->GetGroupName());
    xmlDef->set_name(iter->GetProxyName());
    xmlDef->set_xml(xmlContent.str());

    iter->GoToNextItem();
    }
  iter->Delete();
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::Push(vtkSMMessage* msg)
{
  vtkTimerLog::MarkStartEvent("vtkSIProxyDefinitionManager Load Definitions");
  // Init and local vars
  this->Internals->Clear();
  this->InternalsFlatten->Clear();
  vtkNew<vtkPVXMLParser> parser;

  // Fill the definition with the content of the state
  int size = msg->ExtensionSize(ProxyDefinitionState::xml_definition_proxy);
  const ProxyDefinitionState_ProxyXMLDefinition *xmlDef;
  for(int i=0; i < size; i++)
    {
    xmlDef = &msg->GetExtension(ProxyDefinitionState::xml_definition_proxy, i);
    parser->Parse(xmlDef->xml().c_str());
    this->AddElement( xmlDef->group().c_str(), xmlDef->name().c_str(),
                      parser->GetRootElement());
    }

  // Manage custom ones
  int custom_size = msg->ExtensionSize(ProxyDefinitionState::xml_custom_definition_proxy);
  for(int i=0; i < custom_size; i++)
    {
    xmlDef = &msg->GetExtension(ProxyDefinitionState::xml_custom_definition_proxy, i);
    parser->Parse(xmlDef->xml().c_str());
    this->AddCustomProxyDefinitionInternal(
      xmlDef->group().c_str(), xmlDef->name().c_str(), parser->GetRootElement());
    }

  if (custom_size > 0)
    {
    this->InvokeEvent(vtkSIProxyDefinitionManager::CompoundProxyDefinitionsUpdated);
    }

  this->InvokeEvent(vtkSIProxyDefinitionManager::ProxyDefinitionsUpdated);
  vtkTimerLog::MarkEndEvent("vtkSIProxyDefinitionManager Load Definitions");
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::OnPluginLoaded(
  vtkObject*, unsigned long, void* calldata)
{
  this->HandlePlugin(reinterpret_cast<vtkPVPlugin*>(calldata));
}

//---------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::HandlePlugin(vtkPVPlugin* plugin)
{
  vtkPVServerManagerPluginInterface* smplugin =
    dynamic_cast<vtkPVServerManagerPluginInterface*>(plugin);
  if (smplugin)
    {
    std::vector<std::string> xmls;
    smplugin->GetXMLs(xmls);

    // Make sure only the SERVER is processing the XML proxy definition
    if(this->Internals->EnableXMLProxyDefinitionUpdate)
      {
      for (size_t cc=0; cc < xmls.size(); cc++)
        {
        this->LoadConfigurationXMLFromString(xmls[cc].c_str(),
          // if GetPluginName() == vtkPVInitializerPlugin, it implies that it's
          // the ParaView core and should not be treated as plugin.
          strcmp(plugin->GetPluginName(), "vtkPVInitializerPlugin") != 0);
        }

      // Make sure we invalidate any cached flatten version of our proxy definition
      this->InternalsFlatten->Clear();
      }
    }
}
//---------------------------------------------------------------------------
bool vtkSIProxyDefinitionManager::HasDefinition( const char* groupName,
                                                 const char* proxyName)
{
  return this->Internals->HasCustomDefinition(groupName, proxyName) ||
      this->Internals->HasCoreDefinition(groupName, proxyName);
}

//---------------------------------------------------------------------------
// For now we dynamically convert InformationHelper
// into the correct si_class and attribute sets.
// THIS CODE MUST BE REMOVED once InformationHelper have been removed from
// legacy XML
void vtkSIProxyDefinitionManager::PatchXMLProperty(vtkPVXMLElement* propElement)
{
  vtkPVXMLElement* informationHelper = NULL;

  // Search InformationHelper XML element
  for(unsigned int i=0; i < propElement->GetNumberOfNestedElements(); ++i)
    {
    vtkPVXMLElement* currentElement = propElement->GetNestedElement(i);
    if ( std::string(currentElement->GetName()).find("Helper") !=
         std::string::npos)
      {
      informationHelper = currentElement;
      break;
      }
    }

  // Process InformationHelper
  if(informationHelper)
    {
    if(  !strcmp(informationHelper->GetName(),"StringArrayHelper")
      || !strcmp(informationHelper->GetName(),"DoubleArrayInformationHelper")
      || !strcmp(informationHelper->GetName(),"IntArrayInformationHelper")
      || !strcmp(informationHelper->GetName(),"IdTypeArrayInformationHelper"))
      {
      propElement->SetAttribute("si_class", "vtkSIDataArrayProperty");
      }
    else if (!strcmp(informationHelper->GetName(),"TimeStepsInformationHelper"))
      {
      propElement->SetAttribute("si_class", "vtkSITimeStepsProperty");
      }
    else if (!strcmp(informationHelper->GetName(),"TimeRangeInformationHelper"))
      {
      propElement->SetAttribute("si_class", "vtkSITimeRangeProperty");
      }
    else if (!strcmp(informationHelper->GetName(),"SILInformationHelper"))
      {
      propElement->SetAttribute("si_class", "vtkSISILProperty");
      propElement->SetAttribute("subtree", informationHelper->GetAttribute("subtree"));
      }
    else if (!strcmp(informationHelper->GetName(),"ArraySelectionInformationHelper"))
      {
      propElement->SetAttribute("si_class", "vtkSIArraySelectionProperty");
      propElement->SetAttribute("command", informationHelper->GetAttribute("attribute_name"));
      propElement->SetAttribute("number_of_elements_per_command", "2");
      }
    else if(!strcmp(informationHelper->GetName(),"SimpleDoubleInformationHelper")
      || !strcmp(informationHelper->GetName(),"SimpleIntInformationHelper")
      || !strcmp(informationHelper->GetName(),"SimpleStringInformationHelper")
      || !strcmp(informationHelper->GetName(),"SimpleIdTypeInformationHelper"))
      {
      // Nothing to do, just remove them
      }
    else
      {
      cerr << "No SIProperty for the following information helper: "
           << informationHelper->GetName() << endl;
      }

    // Remove InformationHelper from XML
    propElement->RemoveNestedElement(informationHelper);
    }
}
//----------------------------------------------------------------------------
vtkTypeUInt32 vtkSIProxyDefinitionManager::GetReservedGlobalID()
{
  return vtkReservedRemoteObjectIds::RESERVED_PROXY_DEFINITION_MANAGER_ID;
}
//----------------------------------------------------------------------------
void vtkSIProxyDefinitionManager::EnableXMLProxyDefnitionUpdate(bool enable)
{
  this->Internals->EnableXMLProxyDefinitionUpdate = enable;
}
