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

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

#include "vtkDataSetAttributes.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPVArrayInformation.h"
#include "vtkPVDataInformation.h"
#include "vtkPVDataSetAttributesInformation.h"
#include "vtkPVXMLElement.h"
#include "vtkSMInputArrayDomain.h"
#include "vtkSMProperty.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMStringVectorProperty.h"
#include "vtkSMUncheckedPropertyHelper.h"
#include "vtkStdString.h"
#include "vtkStringList.h"

#include <cassert>
#include <set>
#include <vector>
#include <algorithm>
#include "vtksys/ios/sstream"

vtkStandardNewMacro(vtkSMArrayListDomain);

struct vtkSMArrayListDomainInformationKey
{
  vtkStdString Location;
  vtkStdString Name;
  int Strategy;
};

struct vtkSMArrayListDomainArrayInformation
{
  vtkStdString ArrayName;
  bool IsPartial;

  // data association for this array on input.
  int FieldAssociation;

  // desired association for this array.
  int DomainAssociation;

  int ArrayAttributeType;

  vtkSMArrayListDomainArrayInformation():
    IsPartial(false),
    FieldAssociation(vtkDataObject::POINT),
    DomainAssociation(vtkDataObject::POINT),
    ArrayAttributeType(-1)
    {
    }

  bool operator==(const vtkSMArrayListDomainArrayInformation& other) const
    {
    return this->ArrayName == other.ArrayName &&
      this->IsPartial == other.IsPartial &&
      this->FieldAssociation == other.FieldAssociation &&
      this->DomainAssociation == other.DomainAssociation &&
      this->ArrayAttributeType == other.ArrayAttributeType;
    }

  // helps keep the set sorted in preferred order.
  bool operator < (const vtkSMArrayListDomainArrayInformation& other) const
    {
    if (this->DomainAssociation == other.DomainAssociation)
      {
      if (this->FieldAssociation == other.FieldAssociation)
        {
        if (this->ArrayName == other.ArrayName)
          {
          // don't treat partial and non-partial arrays as different.
          //if (this->IsPartial == other.IsPartial)
          //  {
            return this->ArrayAttributeType < other.ArrayAttributeType;
          //  }
          // return this->IsPartial < other.IsPartial;
          }
        return this->ArrayName < other.ArrayName;
        }
      return this->FieldAssociation < other.FieldAssociation;
      }
    return this->DomainAssociation < other.DomainAssociation;
    }
};

struct vtkSMArrayListDomainInternals
{
public:
  std::vector<vtkSMArrayListDomainInformationKey> InformationKeys;

  std::vector<int> DataTypes;

  typedef std::vector<vtkSMArrayListDomainArrayInformation> DomainValuesVector;
  DomainValuesVector DomainValues;

  typedef std::set<vtkSMArrayListDomainArrayInformation> DomainValuesSet;

  // Builds the list of acceptable arrays in result.
  // association is the association requested by the domain
  // required_number_of_components is the num-of-comps
  void BuildArrayList(DomainValuesSet & result,
    vtkSMArrayListDomain* self,
    int association,
    int required_number_of_components,
    vtkPVDataInformation* dataInfo);

  std::vector<vtkStdString> GetDomainValueStrings()
    {
    std::vector<vtkStdString> values;
    for (DomainValuesVector::const_iterator iter = this->DomainValues.begin(),
      end = this->DomainValues.end(); iter != end; ++iter)
      {
      values.push_back(iter->ArrayName);
      }
    return values;
    }

  const vtkSMArrayListDomainArrayInformation* FindAttribute(int array_attribute)
    {
    for (DomainValuesVector::const_iterator iter = this->DomainValues.begin(),
      end = this->DomainValues.end(); iter != end; ++iter)
      {
      if (iter->ArrayAttributeType == array_attribute)
        {
        return &(*iter);
        }
      }
    return NULL;
    }
private:
  bool IsArrayDataTypeAcceptable(vtkPVArrayInformation* arrayInfo) const
    {
    for (size_t cc=0, max=this->DataTypes.size(); cc < max; ++cc)
      {
      if (this->DataTypes[cc] == VTK_VOID ||
          this->DataTypes[cc] == arrayInfo->GetDataType())
        {
        return true;
        }
      }
    return (this->DataTypes.size() == 0);
    }

  bool AreArrayInformationKeysAcceptable(vtkPVArrayInformation* arrayInfo)
    {
    for (size_t cc=0, max=this->InformationKeys.size(); cc < max; ++cc)
      {
      vtkSMArrayListDomainInformationKey& key = this->InformationKeys[cc];
      int hasInfo = arrayInfo->HasInformationKey(key.Location, key.Name);
      if (hasInfo && key.Strategy == vtkSMArrayListDomain::REJECT_KEY)
        {
        return false;
        }
      if (!hasInfo && key.Strategy == vtkSMArrayListDomain::NEED_KEY)
        {
        return false;
        }
      }
    return true;
    }
};

//---------------------------------------------------------------------------
void vtkSMArrayListDomainInternals::BuildArrayList(
  vtkSMArrayListDomainInternals::DomainValuesSet & result,
  vtkSMArrayListDomain* self,
  int association,
  int required_number_of_components,
  vtkPVDataInformation* dataInfo)
{
  if (self->GetNoneString() && result.size() == 0)
    {
    vtkSMArrayListDomainArrayInformation info;
    info.ArrayName = self->GetNoneString();
    info.IsPartial = false;
    info.FieldAssociation = 0;
    info.DomainAssociation = 0;
    result.insert(info);
    }

  // iterate over attributes arrays in dataInfo and add acceptable arrays to the
  // domain.
  for (int type=vtkSMInputArrayDomain::POINT;
    type < vtkSMInputArrayDomain::NUMBER_OF_ATTRIBUTE_TYPES;
    type++)
    {
    if (type == vtkSMInputArrayDomain::ANY)
      {
      continue;
      }
    vtkPVDataSetAttributesInformation* attrInfo = dataInfo->GetAttributeInformation(type);
    int acceptable_as = type;
    if (attrInfo == NULL ||
      !vtkSMInputArrayDomain::IsAttributeTypeAcceptable(association, type, &acceptable_as))
      {
      continue;
      }
    assert(acceptable_as != vtkSMInputArrayDomain::ANY);

    // iterate over all arrays and add them to the list, if acceptable.
    for (int idx=0, maxIdx=attrInfo->GetNumberOfArrays(); idx < maxIdx; ++idx)
      {
      vtkPVArrayInformation* arrayInfo = attrInfo->GetArrayInformation(idx);

      // First check if the array is acceptable based on the component requirements.
      if (arrayInfo == NULL||
        !vtkSMInputArrayDomain::IsArrayAcceptable(required_number_of_components, arrayInfo))
        {
        continue;
        }

      // Next, check if the array is acceptable based on the data-type
      // limitations specified on self.
      if (!this->IsArrayDataTypeAcceptable(arrayInfo))
        {
        continue;
        }

      // Next, check if the array is acceptable based on the information-key
      // magic.
      if (!this->AreArrayInformationKeysAcceptable(arrayInfo))
        {
        continue;
        }

      // ACCEPTABLE ARRAY FOUND!!!
      // Add it to the list.
      if (required_number_of_components == 0 ||
          required_number_of_components == arrayInfo->GetNumberOfComponents())
        {
        // the array is directly acceptable (no need to split out components)
        vtkSMArrayListDomainArrayInformation info;
        info.ArrayName = arrayInfo->GetName();
        info.IsPartial = (arrayInfo->GetIsPartial() != 0);
        info.FieldAssociation = acceptable_as;
        info.DomainAssociation = type;
        info.ArrayAttributeType = attrInfo->IsArrayAnAttribute(idx);
        result.insert(info);
        }
      else
        {
        // array has number of components that doesn't directly match the required
        // component count. The array is being accepted due to automatic property
        // conversion. So we need to split up components and add them individually.
        assert(required_number_of_components == 1 &&
               vtkSMInputArrayDomain::GetAutomaticPropertyConversion() == true &&
               arrayInfo->GetNumberOfComponents() > 1);

        for (int cc=0, maxCC=arrayInfo->GetNumberOfComponents();
            cc <= maxCC; ++cc)
          {
          if (cc == maxCC && arrayInfo->GetDataType() == VTK_STRING)
            {
            // magnitude is added only for numeric arrays.
            continue;
            }
          vtkSMArrayListDomainArrayInformation info;
          info.ArrayName = vtkSMArrayListDomain::CreateMangledName(arrayInfo, cc);
          info.IsPartial = (arrayInfo->GetIsPartial() != 0);
          info.FieldAssociation = acceptable_as;
          info.DomainAssociation = type;
          info.ArrayAttributeType = attrInfo->IsArrayAnAttribute(idx);
          result.insert(info);
          }
        }
      } // end of for each array
    } // end of for each attribute type
}

//---------------------------------------------------------------------------
vtkSMArrayListDomain::vtkSMArrayListDomain()
{
  this->AttributeType = vtkDataSetAttributes::SCALARS;
  this->InputDomainName = 0;
  this->NoneString = 0;
  this->ALDInternals = new vtkSMArrayListDomainInternals;
  this->PickFirstAvailableArrayByDefault = true;
}

//---------------------------------------------------------------------------
vtkSMArrayListDomain::~vtkSMArrayListDomain()
{
  this->SetInputDomainName(0);
  this->SetNoneString(0);
  delete this->ALDInternals;
  this->ALDInternals = NULL;
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::IsArrayPartial(unsigned int idx)
{
  if (static_cast<size_t>(idx) < this->ALDInternals->DomainValues.size())
    {
    return this->ALDInternals->DomainValues[idx].IsPartial? 1 : 0;
    }

  vtkErrorMacro("Index out of range: " << idx);
  return 0;
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::GetDomainAssociation(unsigned int idx )
{
  if (static_cast<size_t>(idx) < this->ALDInternals->DomainValues.size())
    {
    return this->ALDInternals->DomainValues[idx].DomainAssociation;
    }

  vtkErrorMacro("Index out of range: " << idx);
  return -1;
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::GetFieldAssociation(unsigned int idx)
{
  if (static_cast<size_t>(idx) < this->ALDInternals->DomainValues.size())
    {
    return this->ALDInternals->DomainValues[idx].FieldAssociation;
    }

  vtkErrorMacro("Index out of range: " << idx);
  return -1;
}

//---------------------------------------------------------------------------
void vtkSMArrayListDomain::Update(vtkSMProperty*)
{
  vtkSMProperty* input = this->GetRequiredProperty("Input");
  if (!input)
    {
    vtkErrorMacro("Missing require property 'Input'. Update failed.");
    return;
    }

  vtkPVDataInformation* dataInfo = this->GetInputDataInformation("Input");
  if (!dataInfo)
    {
    return;
    }

  vtkSMProperty* fieldDataSelection = this->GetRequiredProperty("FieldDataSelection");
  vtkSMInputArrayDomain* iad = this->InputDomainName?
    vtkSMInputArrayDomain::SafeDownCast(input->GetDomain(this->InputDomainName)):
    vtkSMInputArrayDomain::SafeDownCast(input->FindDomain("vtkSMInputArrayDomain"));

  int association = vtkSMInputArrayDomain::ANY;
  int required_number_of_components = 0;
  if (iad)
    {
    association = iad->GetAttributeType();
    required_number_of_components = iad->GetNumberOfComponents();
    }

  if (fieldDataSelection)
    {
    vtkSMUncheckedPropertyHelper helper(fieldDataSelection);
    if (helper.GetNumberOfElements()==1)
      {
      association = helper.GetAsInt(0);
      }
    else if (helper.GetNumberOfElements() == 5)
      {
      association = helper.GetAsInt(3);
      }
    else
      {
      vtkWarningMacro("FieldDataSelection property not supported, will be ignored.");
      }
    }

  // validate association.
  if (association < vtkSMInputArrayDomain::POINT ||
    association >= vtkSMInputArrayDomain::NUMBER_OF_ATTRIBUTE_TYPES)
    {
    vtkWarningMacro("Invalid association. Ignoring.");
    association = vtkSMInputArrayDomain::ANY;
    }

  if (required_number_of_components <0)
    {
    required_number_of_components = 0;
    }

  // we use a set so that the list gets sorted as well as helps us
  // avoid duplicates esp. when processing two datainformation objects.
  vtkSMArrayListDomainInternals::DomainValuesSet set;
  this->ALDInternals->BuildArrayList(set, this,
    association, required_number_of_components, dataInfo);

  vtkPVDataInformation* extraInfo = this->GetExtraDataInformation();
  if (extraInfo)
    {
    this->ALDInternals->BuildArrayList(set, this,
      association, required_number_of_components, extraInfo);
    }

  vtkSMArrayListDomainInternals::DomainValuesVector values;
  values.insert(values.end(), set.begin(), set.end());
  if (values != this->ALDInternals->DomainValues)
    {
    this->ALDInternals->DomainValues = values;
    this->SetStrings(this->ALDInternals->GetDomainValueStrings());
    // FIXME: there is still a possibility that the above may not trigger
    // DomainModified(). How do we handle that without unnecessarily
    // firing the signal twice?
    }
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::ReadXMLAttributes(
  vtkSMProperty* prop, vtkPVXMLElement* element)
{
  if (!this->Superclass::ReadXMLAttributes(prop, element))
    {
    return 0;
    }

  const char* input_domain_name =
    element->GetAttribute("input_domain_name");
  if (input_domain_name)
    {
    this->SetInputDomainName(input_domain_name);
    }

  // Search for attribute type with matching name.
  const char* none_string = element->GetAttribute("none_string");
  if (none_string)
    {
    this->SetNoneString(none_string);
    }

  const char* key_locations = element->GetAttribute("key_locations");
  const char* key_names = element->GetAttribute("key_names");
  const char* key_strategies = element->GetAttribute("key_strategies");
  if(key_locations == NULL)
    {
    // Default value : add a needed information key vtkAbstractArray::GUI_HIDE
    this->AddInformationKey("vtkAbstractArray", "GUI_HIDE", vtkSMArrayListDomain::REJECT_KEY);
    }
  else
    {
    std::stringstream locations(key_locations);
    std::stringstream names(key_names);
    std::stringstream strategies(key_strategies);
    std::string location, name, strategy, last_strategy;
    int strat;
    // default value for the strategy
    last_strategy = "need_key";
    while(locations >> location)
      {
      if(! (names >> name) )
        {
        vtkErrorMacro("The number of key names must be equal to the number of key locations");
        break;
        }
      if(! (strategies >> strategy))
        {
        strategy = last_strategy;
        }
      last_strategy = strategy;

      if(strategy == "need_key" || strategy == "NEED_KEY")
        {
        strat = vtkSMArrayListDomain::NEED_KEY;
        }
      else if(strategy == "reject_key" || strategy == "REJECT_KEY")
        {
        strat = vtkSMArrayListDomain::REJECT_KEY;
        }
      else
        {
        // maybe the strategy was coded as an integer.
        strat=atoi(strategy.c_str());
        }
      this->AddInformationKey(location.c_str(), name.c_str(), strat);
      }
    }

  // Search for attribute type to use when picking a default.
  const char* attribute_type = element->GetAttribute("attribute_type");
  if (attribute_type)
    {
    for(unsigned int i=0; i<vtkDataSetAttributes::NUM_ATTRIBUTES; ++i)
      {
      if(strcmp(vtkDataSetAttributes::GetAttributeTypeAsString(i),
                attribute_type) == 0)
        {
        this->SetAttributeType(i);
        break;
        }
      }
    }

  const char* data_type = element->GetAttribute("data_type");
  if (data_type)
    {
    // data_type can be a space delimited list of types
    // vlaid for the domain
    vtksys_ios::istringstream dataTypeStream(data_type);

    while (dataTypeStream.good())
      {
      std::string thisType;
      dataTypeStream >> thisType;

      if ( thisType=="VTK_VOID" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_VOID);
        }
      else if ( thisType=="VTK_BIT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_BIT);
        }
      else if ( thisType=="VTK_CHAR" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_CHAR);
        }
      else if ( thisType=="VTK_SIGNED_CHAR" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_SIGNED_CHAR);
        }
      else if ( thisType=="VTK_UNSIGNED_CHAR" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_CHAR);
        }
      else if ( thisType=="VTK_SHORT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_SHORT);
        }
      else if ( thisType=="VTK_UNSIGNED_SHORT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_SHORT);
        }
      else if ( thisType=="VTK_INT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_INT);
        }
      else if ( thisType=="VTK_UNSIGNED_INT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_INT);
        }
      else if ( thisType=="VTK_LONG" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_LONG);
        }
      else if ( thisType=="VTK_FLOAT" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_FLOAT);
        }
      else if ( thisType=="VTK_DOUBLE" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_DOUBLE);
        }
      else if ( thisType=="VTK_ID_TYPE" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_ID_TYPE);
        }
      else if ( thisType=="VTK_STRING" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_STRING);
        }
      else if ( thisType=="VTK_OPAQUE" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_OPAQUE);
        }
      else if ( thisType=="VTK_LONG_LONG" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_LONG_LONG);
        }
      else if ( thisType=="VTK_UNSIGNED_LONG_LONG" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_LONG_LONG);
        }
      else if ( thisType=="VTK___INT64" )
        {
        this->ALDInternals->DataTypes.push_back(VTK___INT64);
        }
      else if ( thisType=="VTK_UNSIGNED___INT64" )
        {
        this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED___INT64);
        }
      else if ( thisType=="VTK_DATA_ARRAY" )
        {
          this->ALDInternals->DataTypes.push_back(VTK_BIT);
          this->ALDInternals->DataTypes.push_back(VTK_CHAR);
          this->ALDInternals->DataTypes.push_back(VTK_SIGNED_CHAR);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_CHAR);
          this->ALDInternals->DataTypes.push_back(VTK_SHORT);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_SHORT);
          this->ALDInternals->DataTypes.push_back(VTK_INT);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_INT);
          this->ALDInternals->DataTypes.push_back(VTK_LONG);
          this->ALDInternals->DataTypes.push_back(VTK_FLOAT);
          this->ALDInternals->DataTypes.push_back(VTK_DOUBLE);
          this->ALDInternals->DataTypes.push_back(VTK_ID_TYPE);
          this->ALDInternals->DataTypes.push_back(VTK_LONG_LONG);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_LONG);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED_LONG_LONG);
          this->ALDInternals->DataTypes.push_back(VTK___INT64);
          this->ALDInternals->DataTypes.push_back(VTK_UNSIGNED___INT64);
        }
      else
        {
        int maybeType=atoi(thisType.c_str()); //?
        this->ALDInternals->DataTypes.push_back(maybeType);
        }
      }
    }

  return 1;
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::SetDefaultValues(vtkSMProperty* prop, bool use_unchecked_values)
{
  vtkSMStringVectorProperty* svp = vtkSMStringVectorProperty::SafeDownCast(prop);
  if (!svp)
    {
    return 0;
    }

  vtkSMPropertyHelper helper(prop);
  helper.SetUseUnchecked(use_unchecked_values);

  // If vtkSMStringVectorProperty has a default value which is in domain, just
  // use it.
  const char* defaultValue = svp->GetDefaultValue(0);
  unsigned int temp;
  if (defaultValue && this->IsInDomain(defaultValue, temp))
    {
    if (helper.GetNumberOfElements() == 5)
      {
      helper.Set(4, defaultValue);
      return 1;
      }
    else if (helper.GetNumberOfElements() == 1)
      {
      helper.Set(0, defaultValue);
      return 1;
      }
    }

  const vtkSMArrayListDomainArrayInformation* info =
    this->ALDInternals->FindAttribute(this->AttributeType);
  if (info == NULL &&
    this->ALDInternals->DomainValues.size() > 0 &&
    this->PickFirstAvailableArrayByDefault == true)
    {
    info = &this->ALDInternals->DomainValues[0];
    }
  if (info)
    {
    if (helper.GetNumberOfElements() == 5)
      {
      vtkNew<vtkStringList> values;
      if (use_unchecked_values)
        {
        svp->GetUncheckedElements(values.GetPointer());
        }
      else
        {
        svp->GetElements(values.GetPointer());
        }

      vtksys_ios::ostringstream ass;
      ass << info->FieldAssociation;
      values->SetString(3, ass.str().c_str());
      values->SetString(4, info->ArrayName.c_str());
      if (use_unchecked_values)
        {
        svp->SetUncheckedElements(values.GetPointer());
        }
      else
        {
        svp->SetElements(values.GetPointer());
        }
      return 1;
      }
    else if (svp->GetNumberOfElements() == 1)
      {
      helper.Set(0, info->ArrayName.c_str());
      return 1;
      }
    }

  return this->Superclass::SetDefaultValues(prop, use_unchecked_values);
}

//---------------------------------------------------------------------------
unsigned int vtkSMArrayListDomain::AddInformationKey(const char* location, const char *name, int strategy)
{
  vtkSMArrayListDomainInformationKey key;
  key.Location = location;
  key.Name = name;
  key.Strategy = strategy;
  this->ALDInternals->InformationKeys.push_back(key);
  return static_cast<unsigned int>(this->ALDInternals->InformationKeys.size() - 1);
}

//---------------------------------------------------------------------------
unsigned int vtkSMArrayListDomain::RemoveInformationKey(const char* location, const char *name)
{
  std::vector<vtkSMArrayListDomainInformationKey>::iterator it
    = this->ALDInternals->InformationKeys.begin();
  int index = 0;
  while(it != this->ALDInternals->InformationKeys.end())
    {
    vtkSMArrayListDomainInformationKey& key = *it;
    if(key.Location == location && key.Name == name)
      {
      this->ALDInternals->InformationKeys.erase(it);
      return index;
      }
    it++;
    index++;
    }
  return 0;
}

//---------------------------------------------------------------------------
unsigned int vtkSMArrayListDomain::GetNumberOfInformationKeys()
{
  return static_cast<unsigned int>(this->ALDInternals->InformationKeys.size());
}

//---------------------------------------------------------------------------
void vtkSMArrayListDomain::RemoveAllInformationKeys()
{
  this->ALDInternals->InformationKeys.clear();
}

//---------------------------------------------------------------------------
const char* vtkSMArrayListDomain::GetInformationKeyLocation(unsigned int index)
{
  if(index < this->ALDInternals->InformationKeys.size())
    {
    return this->ALDInternals->InformationKeys[index].Location;
    }
  return NULL;
}

//---------------------------------------------------------------------------
const char* vtkSMArrayListDomain::GetInformationKeyName(unsigned int index)
{
  if(index < this->ALDInternals->InformationKeys.size())
    {
    return this->ALDInternals->InformationKeys[index].Name;
    }
  return NULL;
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::GetInformationKeyStrategy(unsigned int index)
{
  if(index < this->ALDInternals->InformationKeys.size())
    {
    return this->ALDInternals->InformationKeys[index].Strategy;
    }
  return -1;
}

//---------------------------------------------------------------------------
void vtkSMArrayListDomain::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "AttributeType: " << this->AttributeType << endl;
  int nDataTypes=static_cast<int>(this->ALDInternals->DataTypes.size());
  for (int i=0; i<nDataTypes; ++i)
    {
    os << indent << "DataType: " << this->ALDInternals->DataTypes[i] << endl;
    }

  for(unsigned int i=0; i<this->ALDInternals->InformationKeys.size(); i++)
    {
    vtkSMArrayListDomainInformationKey& key = this->ALDInternals->InformationKeys[i];
    os << key.Location << "::" << key.Name << " ";
    if(key.Strategy == vtkSMArrayListDomain::NEED_KEY)
      {
      os << "NEED_KEY";
      }
    else if(key.Strategy == vtkSMArrayListDomain::REJECT_KEY)
      {
      os << "REJECT_KEY";
      }
    else
      {
      os << "UNKNOWN KEY STRATEGY : " << key.Strategy;
      }
    os << endl;
    }
}


//---------------------------------------------------------------------------
vtkStdString vtkSMArrayListDomain::CreateMangledName(
  vtkPVArrayInformation *arrayInfo, int component)
{
  vtksys_ios::ostringstream stream;
  if ( component != arrayInfo->GetNumberOfComponents() )
    {
    stream << arrayInfo->GetName() << "_" <<
      arrayInfo->GetComponentName(component);
    }
  else
    {
    stream << arrayInfo->GetName() << "_Magnitude";
    }
  return stream.str();
}

//---------------------------------------------------------------------------
vtkStdString vtkSMArrayListDomain::ArrayNameFromMangledName(
  const char* name)
{
  vtkStdString extractedName = name;
  size_t pos = extractedName.rfind("_");
  if (pos == vtkStdString::npos)
    {
    return extractedName;
    }
  return extractedName.substr(0,pos);
}

//---------------------------------------------------------------------------
int vtkSMArrayListDomain::ComponentIndexFromMangledName(
  vtkPVArrayInformation *info, const char* name)
{
  vtkStdString extractedName = name;
  size_t pos = extractedName.rfind("_");
  if (pos == vtkStdString::npos)
    {
    return -1;
    }
  vtkStdString compName = extractedName.substr(pos+1,extractedName.length()-pos);
  int numComps = info->GetNumberOfComponents();
  if ( compName == "Magnitude" )
    {
    return numComps;
    }
  for ( int i=0; i < numComps; ++i)
    {
    if ( compName == info->GetComponentName(i) )
      {
      return i;
      }
    }
  return -1;
}
