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

  Program:   Visualization Toolkit
  Module:    vtkProxyAdapter.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 "vtkProxyAdapter.h"
#include "vtkPropertyAdapter.h"
#include "vtkPropertyGroupAdapter.h"
#include "vtkSMOrderedPropertyIterator.h"
#include "vtkSMPropertyGroup.h"
#include "vtkSMProxy.h"
#include "vtkSmartPointer.h"
#include <memory>
#include <vector>

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkProxyAdapter);

//-----------------------------------------------------------------------------
vtkProxyAdapter::vtkProxyAdapter() = default;

//-----------------------------------------------------------------------------
vtkProxyAdapter::~vtkProxyAdapter() = default;

//-----------------------------------------------------------------------------
void vtkProxyAdapter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "SMProxy " << this->SMProxy;
  if (this->SMProxy)
  {
    this->SMProxy->PrintSelf(os, indent.GetNextIndent());
  }
}

//-----------------------------------------------------------------------------
void vtkProxyAdapter::SetSMProxy(vtkSMProxy* proxy)
{
  // Add groups
  // Groups with the same name. Make sure PropertyGroups has only one instance of each group
  std::map<vtkSMProperty*, std::string> property2Groups;
  std::map<std::string, vtkSmartPointer<vtkPropertyGroupAdapter>> name2Groups;
  for (int i = 0; i < proxy->GetNumberOfPropertyGroups(); i++)
  {
    vtkSMPropertyGroup* smgroup = proxy->GetPropertyGroup(i);
    std::string label = smgroup->GetXMLLabel(); // TODO is a empty xml label possible ?

    auto iter = name2Groups.find(label);
    vtkSmartPointer<vtkPropertyGroupAdapter> group;
    if (iter == name2Groups.end())
    {
      group = vtk::TakeSmartPointer(vtkPropertyGroupAdapter::New());
      group->SetSMPropertyGroup(smgroup);
      if (group->GetName().empty())
      {
        group->SetName("__unnamed_group_" + std::to_string(i));
      }
      this->PropertyGroups.emplace_back(group);
      name2Groups[label] = group;
    }
    else
    {
      group = iter->second;
    }

    for (int i = 0; i < smgroup->GetNumberOfProperties(); i++)
    {
      property2Groups[smgroup->GetProperty(i)] = label;
    }
  }

  // Add properties
  vtkNew<vtkSMOrderedPropertyIterator> iterator;
  iterator->SetProxy(proxy);
  for (iterator->Begin(); !iterator->IsAtEnd(); iterator->Next())
  {
    vtkSMProperty* smproperty = iterator->GetProperty();
    vtkSmartPointer<vtkPropertyAdapter> property = vtk::TakeSmartPointer(vtkPropertyAdapter::New());
    property->SetSMProperty(smproperty);

    auto iter = property2Groups.find(smproperty);

    if (iter != property2Groups.end())
    {
      try
      {
        vtkDebugMacro(<< "Set property " << property->GetLabel() << " in group " <<   iter->second );
        property->SetPropertyGroup(name2Groups.at(iter->second));
      }
      catch (std::out_of_range& e)
      {
        vtkLogF(ERROR, " invalid relation between property and property group");
      }
    }

    this->Properties.emplace_back(property);
  }

  this->SMProxy = proxy;
}

//------------------------------------------------------------------------------
std::vector<vtkSmartPointer<vtkPropertyAdapter>> vtkProxyAdapter::GetProperties() const
{
  return this->Properties;
}

//------------------------------------------------------------------------------
std::vector<vtkSmartPointer<vtkPropertyGroupAdapter>> vtkProxyAdapter::GetPropertyGroups() const
{
  return this->PropertyGroups;
}

//------------------------------------------------------------------------------
std::string vtkProxyAdapter::GetGroup() const
{
  return this->SMProxy->GetXMLGroup();
}

//------------------------------------------------------------------------------
std::string vtkProxyAdapter::GetName() const
{
  return this->SMProxy->GetXMLName();
}

//------------------------------------------------------------------------------
void vtkProxyAdapter::Move(vtkProxyAdapter* other)
{
  this->SMProxy = other->SMProxy;
  other->SMProxy = nullptr;
  this->Properties = std::move(other->Properties);
  this->PropertyGroups = std::move(other->PropertyGroups);
}
