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

  Program:   Visualization Toolkit
  Module:    vtkDataSetAttributes.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 "vtkDataSetAttributes.h"

#include "vtkArrayDispatch.h"
#include "vtkArrayIteratorIncludes.h"
#include "vtkAssume.h"
#include "vtkCell.h"
#include "vtkCharArray.h"
#include "vtkDataArrayAccessor.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkIntArray.h"
#include "vtkLongArray.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkShortArray.h"
#include "vtkStructuredExtent.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnsignedIntArray.h"
#include "vtkUnsignedLongArray.h"
#include "vtkUnsignedShortArray.h"
#include <vector>

namespace
{
  // pair.first it used to indicate if pair.second is valid.
  typedef  std::vector<std::pair<bool, vtkStdString> > vtkInternalComponentNameBase;
}

class vtkDataSetAttributes::vtkInternalComponentNames : public vtkInternalComponentNameBase {};

vtkStandardNewMacro(vtkDataSetAttributes);

//--------------------------------------------------------------------------
const char vtkDataSetAttributes
::AttributeNames[vtkDataSetAttributes::NUM_ATTRIBUTES][12] =
{ "Scalars",
  "Vectors",
  "Normals",
  "TCoords",
  "Tensors",
  "GlobalIds",
  "PedigreeIds",
  "EdgeFlag"
};

const char vtkDataSetAttributes
::LongAttributeNames[vtkDataSetAttributes::NUM_ATTRIBUTES][35] =
{ "vtkDataSetAttributes::SCALARS",
  "vtkDataSetAttributes::VECTORS",
  "vtkDataSetAttributes::NORMALS",
  "vtkDataSetAttributes::TCOORDS",
  "vtkDataSetAttributes::TENSORS",
  "vtkDataSetAttributes::GLOBALIDS",
  "vtkDataSetAttributes::PEDIGREEIDS",
  "vtkDataSetAttributes::EDGEFLAG"
};

//--------------------------------------------------------------------------
// Construct object with copying turned on for all data.
vtkDataSetAttributes::vtkDataSetAttributes()
{
  int attributeType;
  for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    this->AttributeIndices[attributeType] = -1;
    this->CopyAttributeFlags[COPYTUPLE][attributeType] = 1;
    this->CopyAttributeFlags[INTERPOLATE][attributeType] = 1;
    this->CopyAttributeFlags[PASSDATA][attributeType] = 1;
  }

  //Global IDs should not be interpolated because they are labels, not "numbers"
  //Global IDs should not be copied either, unless doing so preserves meaning.
  //Passing through is ussually OK because it is 1:1.
  this->CopyAttributeFlags[COPYTUPLE][GLOBALIDS] = 0;
  this->CopyAttributeFlags[INTERPOLATE][GLOBALIDS] = 0;

  //Pedigree IDs should not be interpolated because they are labels, not "numbers"
  //Pedigree IDs may be copied since they do not require 1:1 mapping.
  this->CopyAttributeFlags[INTERPOLATE][PEDIGREEIDS] = 0;

  this->TargetIndices=nullptr;

}

//--------------------------------------------------------------------------
// Destructor for the vtkDataSetAttributes objects.
vtkDataSetAttributes::~vtkDataSetAttributes()
{
  this->Initialize();
  delete[] this->TargetIndices;
  this->TargetIndices = nullptr;
}

//--------------------------------------------------------------------------
// Turn on copying of all data.
void vtkDataSetAttributes::CopyAllOn(int ctype)
{
  this->vtkFieldData::CopyAllOn();
  this->SetCopyScalars(1, ctype);
  this->SetCopyVectors(1, ctype);
  this->SetCopyNormals(1, ctype);
  this->SetCopyTCoords(1, ctype);
  this->SetCopyTensors(1, ctype);
  this->SetCopyGlobalIds(1, ctype);
  this->SetCopyPedigreeIds(1, ctype);
}

//--------------------------------------------------------------------------
// Turn off copying of all data.
void vtkDataSetAttributes::CopyAllOff(int ctype)
{
  this->vtkFieldData::CopyAllOff();
  this->SetCopyScalars(0, ctype);
  this->SetCopyVectors(0, ctype);
  this->SetCopyNormals(0, ctype);
  this->SetCopyTCoords(0, ctype);
  this->SetCopyTensors(0, ctype);
  this->SetCopyGlobalIds(0, ctype);
  this->SetCopyPedigreeIds(0, ctype);
}

//--------------------------------------------------------------------------
// Deep copy of data (i.e., create new data arrays and
// copy from input data). Note that attribute data is
// not copied.
void vtkDataSetAttributes::DeepCopy(vtkFieldData *fd)
{
  this->Initialize(); //free up memory

  vtkDataSetAttributes* dsa = vtkDataSetAttributes::SafeDownCast(fd);
  // If the source is a vtkDataSetAttributes
  if (dsa)
  {
    int numArrays = fd->GetNumberOfArrays();
    int attributeType, i;
    vtkAbstractArray *data, *newData;

    // Allocate space for numArrays
    this->AllocateArrays(numArrays);
    for (i=0; i < numArrays; i++ )
    {
      data = fd->GetAbstractArray(i);
      newData = data->NewInstance(); //instantiate same type of object
      newData->DeepCopy(data);
      newData->SetName(data->GetName());
      this->AddArray(newData);
      newData->Delete();
    }
    // Copy the copy flags
    for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
    {
      // If an array is an attribute in the source, then mark it as a attribute
      // in the clone as well.
      this->AttributeIndices[attributeType] = dsa->AttributeIndices[attributeType];

      this->CopyAttributeFlags[COPYTUPLE][attributeType] =
        dsa->CopyAttributeFlags[COPYTUPLE][attributeType];
      this->CopyAttributeFlags[INTERPOLATE][attributeType] =
        dsa->CopyAttributeFlags[INTERPOLATE][attributeType];
      this->CopyAttributeFlags[PASSDATA][attributeType] =
        dsa->CopyAttributeFlags[PASSDATA][attributeType];
    }
    this->CopyFlags(dsa);
  }
  // If the source is field data, do a field data copy
  else
  {
    this->vtkFieldData::DeepCopy(fd);
  }
}

//--------------------------------------------------------------------------
// Shallow copy of data (i.e., use reference counting).
void vtkDataSetAttributes::ShallowCopy(vtkFieldData *fd)
{
  this->Initialize(); //free up memory

  vtkDataSetAttributes* dsa = vtkDataSetAttributes::SafeDownCast(fd);
  // If the source is a vtkDataSetAttributes
  if (dsa)
  {
    int numArrays = fd->GetNumberOfArrays();
    int attributeType, i;

    // Allocate space for numArrays
    this->AllocateArrays(numArrays);
    this->NumberOfActiveArrays = 0;
    for (i=0; i < numArrays; i++ )
    {
      this->NumberOfActiveArrays++;
      this->SetArray(i, fd->GetAbstractArray(i));
    }

    // Copy the copy flags
    for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
    {
      // If an array is an attribute in the source, then mark it as a attribute
      // in the clone as well.
      this->AttributeIndices[attributeType] = dsa->AttributeIndices[attributeType];

      this->CopyAttributeFlags[COPYTUPLE][attributeType] =
        dsa->CopyAttributeFlags[COPYTUPLE][attributeType];
      this->CopyAttributeFlags[INTERPOLATE][attributeType] =
        dsa->CopyAttributeFlags[INTERPOLATE][attributeType];
      this->CopyAttributeFlags[PASSDATA][attributeType] =
        dsa->CopyAttributeFlags[PASSDATA][attributeType];
    }
    this->CopyFlags(dsa);
  }
  // If the source is field data, do a field data copy
  else
  {
    this->vtkFieldData::ShallowCopy(fd);
  }
}

//--------------------------------------------------------------------------
// Initialize all of the object's data to nullptr
void vtkDataSetAttributes::InitializeFields()
{
  this->vtkFieldData::InitializeFields();

  int attributeType;
  for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    this->AttributeIndices[attributeType] = -1;
    this->CopyAttributeFlags[COPYTUPLE][attributeType] = 1;
    this->CopyAttributeFlags[INTERPOLATE][attributeType] = 1;
    this->CopyAttributeFlags[PASSDATA][attributeType] = 1;
  }
  this->CopyAttributeFlags[COPYTUPLE][GLOBALIDS] = 0;
  this->CopyAttributeFlags[INTERPOLATE][GLOBALIDS] = 0;

  this->CopyAttributeFlags[INTERPOLATE][PEDIGREEIDS] = 0;
}

//--------------------------------------------------------------------------
// Initialize all of the object's data to nullptr
void vtkDataSetAttributes::Initialize()
{
  //
  // We don't modify ourselves because the "ReleaseData" methods depend upon
  // no modification when initialized.
  //

  // Call superclass' Initialize()
  this->vtkFieldData::Initialize();
  //
  // Free up any memory
  // And don't forget to reset the attribute copy flags.
  int attributeType;
  for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    this->AttributeIndices[attributeType] = -1;
    this->CopyAttributeFlags[COPYTUPLE][attributeType] = 1;
    this->CopyAttributeFlags[INTERPOLATE][attributeType] = 1;
    this->CopyAttributeFlags[PASSDATA][attributeType] = 1;
  }
  this->CopyAttributeFlags[COPYTUPLE][GLOBALIDS] = 0;
  this->CopyAttributeFlags[INTERPOLATE][GLOBALIDS] = 0;

  this->CopyAttributeFlags[INTERPOLATE][PEDIGREEIDS] = 0;
}

//--------------------------------------------------------------------------
// This method is used to determine which arrays
// will be copied to this object
vtkFieldData::BasicIterator  vtkDataSetAttributes::ComputeRequiredArrays(
  vtkDataSetAttributes* pd, int ctype)
{
  if ((ctype < COPYTUPLE) || (ctype > PASSDATA))
  {
    vtkErrorMacro("Must call compute required with COPYTUPLE, INTERPOLATE or PASSDATA");
    ctype = COPYTUPLE;
  }

  // We need to do some juggling to find the number of arrays
  // which will be passed.

  // First, find the number of arrays to be copied because they
  // are in the list of _fields_ to be copied (and the actual data
  // pointer is non-nullptr). Also, we keep those indices in a list.
  int* copyFlags = new int[pd->GetNumberOfArrays()];
  int index, i, numArrays = 0;
  for(i=0; i<pd->GetNumberOfArrays(); i++)
  {
    const char* arrayName = pd->GetArrayName(i);
    // If there is no blocker for the given array
    // and both CopyAllOff and CopyOn for that array are not true
    if ( (this->GetFlag(arrayName) != 0) &&
         !(this->DoCopyAllOff && (this->GetFlag(arrayName) != 1)) &&
         pd->GetAbstractArray(i))
    {
      // Cannot interpolate idtype arrays
      if (ctype != INTERPOLATE ||
          pd->GetAbstractArray(i)->GetDataType() != VTK_ID_TYPE)
      {
        copyFlags[numArrays] = i;
        numArrays++;
      }
    }
  }

  // Next, we check the arrays to be copied because they are one of
  // the _attributes_ to be copied (and the data array in non-nullptr).
  // We make sure that we don't count anything twice.
  int alreadyCopied;
  int attributeType, j;
  for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    index = pd->AttributeIndices[attributeType];
    int flag = this->GetFlag(pd->GetArrayName(index));
    // If this attribute is to be copied
    if (this->CopyAttributeFlags[ctype][attributeType] && flag)
    {
      // Find out if it is also in the list of fields to be copied
      // Since attributes can only be vtkDataArray, we use GetArray() call.
      if (pd->GetArray(index))
      {
        alreadyCopied = 0;
        for(i=0; i<numArrays; i++)
        {
          if ( index == copyFlags[i] )
          {
            alreadyCopied = 1;
          }
        }
        // If not, increment the number of arrays to be copied.
        if (!alreadyCopied)
        {
          // Cannot interpolate idtype arrays
          if (ctype != INTERPOLATE ||
              pd->GetArray(index)->GetDataType() != VTK_ID_TYPE)
          {
            copyFlags[numArrays] = index;
            numArrays++;
          }
        }
      }
    }
    // If it is not to be copied and it is in the list (from the
    // previous pass), remove it
    else
    {
      for(i=0; i<numArrays; i++)
      {
        if ( index == copyFlags[i] )
        {
          for(j=i; j<numArrays-1; j++)
          {
            copyFlags[j] = copyFlags[j+1];
          }
          numArrays--;
          i--;
        }
      }
    }
  }

  vtkFieldData::BasicIterator it(copyFlags, numArrays);
  delete[] copyFlags;
  return it;
}

//--------------------------------------------------------------------------
// Pass entire arrays of input data through to output. Obey the "copy"
// flags.
void vtkDataSetAttributes::PassData(vtkFieldData* fd)
{
  if (!fd)
  {
    return;
  }

  vtkDataSetAttributes* dsa = vtkDataSetAttributes::SafeDownCast(fd);

  if (dsa)
  {
    // Create an iterator to iterate over the fields which will
    // be passed, i.e. fields which are either:
    // 1> in the list of _fields_ to be copied or
    // 2> in the list of _attributes_ to be copied.
    // Note that nullptr data arrays are not copied

    vtkFieldData::BasicIterator it = this->ComputeRequiredArrays(dsa, PASSDATA);

    if ( it.GetListSize() > this->NumberOfArrays )
    {
      this->AllocateArrays(it.GetListSize());
    }
    if (it.GetListSize() == 0)
    {
      return;
    }

    // Since we are replacing, remove old attributes
    int attributeType; //will change//
    for(attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
    {
      if (this->CopyAttributeFlags[PASSDATA][attributeType])
      {
        this->RemoveArray(this->AttributeIndices[attributeType]);
        this->AttributeIndices[attributeType] = -1;
      }
    }

    int i, arrayIndex;
    for(i=it.BeginIndex(); !it.End(); i=it.NextIndex())
    {
      arrayIndex = this->AddArray(dsa->GetAbstractArray(i));
      // If necessary, make the array an attribute
      if ( ((attributeType = dsa->IsArrayAnAttribute(i)) != -1 ) &&
           this->CopyAttributeFlags[PASSDATA][attributeType] )
      {
        this->SetActiveAttribute(arrayIndex, attributeType);
      }
    }
  }
  else
  {
    this->vtkFieldData::PassData(fd);
  }
}

//----------------------------------------------------------------------------
namespace {
struct CopyStructuredDataWorker
{
  const int *OutExt;
  const int *InExt;

  CopyStructuredDataWorker(const int *outExt, const int *inExt)
    : OutExt(outExt), InExt(inExt)
  {}

  template <typename Array1T, typename Array2T>
  void operator()(Array1T *dest, Array2T *src)
  {
    if (vtkStructuredExtent::Smaller(this->OutExt, this->InExt))
    {
      // get outExt relative to the inExt to keep the logic simple. This assumes
      // that outExt is a subset of the inExt.
      const int relOutExt[6] = {
        this->OutExt[0] - this->InExt[0],
        this->OutExt[1] - this->InExt[0],
        this->OutExt[2] - this->InExt[2],
        this->OutExt[3] - this->InExt[2],
        this->OutExt[4] - this->InExt[4],
        this->OutExt[5] - this->InExt[4]
      };

      // Give the compiler a hand -- allow optimizations that require both arrays
      // to have the same stride.
      VTK_ASSUME(src->GetNumberOfComponents() == dest->GetNumberOfComponents());

      vtkDataArrayAccessor<Array1T> d(dest);
      vtkDataArrayAccessor<Array2T> s(src);

      const int dims[3] = { this->InExt[1] - this->InExt[0] + 1,
                            this->InExt[3] - this->InExt[2] + 1,
                            this->InExt[5] - this->InExt[4] + 1};

      vtkIdType outTupleIdx = 0;
      for (int outz = relOutExt[4]; outz <= relOutExt[5]; ++outz)
      {
        const vtkIdType zfactor = static_cast<vtkIdType>(outz) * dims[1];
        for (int outy = relOutExt[2]; outy <= relOutExt[3]; ++outy)
        {
          const vtkIdType yfactor = (zfactor + outy) * dims[0];
          for (int outx = relOutExt[0]; outx <= relOutExt[1]; ++outx)
          {
            const vtkIdType inTupleIdx = yfactor + outx;
            for (int comp = 0, max = dest->GetNumberOfComponents();
                 comp < max; ++comp)
            {
              d.Set(outTupleIdx, comp, s.Get(inTupleIdx, comp));
            }
            outTupleIdx++;
          }
        }
      }
    }
    else
    {
      vtkDataArrayAccessor<Array1T> d(dest);
      vtkDataArrayAccessor<Array2T> s(src);

      int writeExt[6];
      memcpy(writeExt, this->OutExt, 6*sizeof(int));
      vtkStructuredExtent::Clamp(writeExt, this->InExt);

      vtkIdType inDims[3] = {this->InExt[1] - this->InExt[0] + 1,
                             this->InExt[3] - this->InExt[2] + 1,
                             this->InExt[5] - this->InExt[4] + 1};
      vtkIdType outDims[3] = {this->OutExt[1] - this->OutExt[0] + 1,
                              this->OutExt[3] - this->OutExt[2] + 1,
                              this->OutExt[5] - this->OutExt[4] + 1};

      for (int idz = writeExt[4]; idz <= writeExt[5]; ++idz)
      {
        vtkIdType inTupleId1 = (idz - this->InExt[4]) * inDims[0] * inDims[1];
        vtkIdType outTupleId1 = (idz - this->OutExt[4]) * outDims[0] * outDims[1];
        for (int idy = writeExt[2]; idy <= writeExt[3]; ++idy)
        {
          vtkIdType inTupleId2 = inTupleId1 + (idy - this->InExt[2]) * inDims[0];
          vtkIdType outTupleId2 = outTupleId1 + (idy - this->OutExt[2]) * outDims[0];
          for (int idx = writeExt[0]; idx <= writeExt[1]; ++idx)
          {
            vtkIdType inTupleIdx = inTupleId2 + idx - this->InExt[0];
            vtkIdType outTupleIdx = outTupleId2 + idx - this->OutExt[0];
            for (int comp = 0, max = dest->GetNumberOfComponents();
                 comp < max; ++comp)
            {
              d.Set(outTupleIdx, comp, s.Get(inTupleIdx, comp));
            }
          }
        }
      }
    }
    dest->DataChanged();
  }
};

} // end anon namespace

//----------------------------------------------------------------------------
template <class iterT>
void vtkDataSetAttributesCopyValues(
  iterT* destIter, const int* outExt, vtkIdType outIncs[3],
  iterT* srcIter, const int* inExt, vtkIdType inIncs[3])
{
  // For vtkDataArray subclasses.
  int data_type_size = srcIter->GetArray()->GetDataTypeSize();
  vtkIdType rowLength = outIncs[1];
  unsigned char *inPtr;
  unsigned char *outPtr;
  unsigned char *inZPtr;
  unsigned char *outZPtr;

  // Get the starting input pointer.
  inZPtr = static_cast<unsigned char*>(srcIter->GetArray()->GetVoidPointer(0));
  // Shift to the start of the subextent.
  inZPtr +=
      (outExt[0] - inExt[0]) * inIncs[0] * data_type_size +
      (outExt[2] - inExt[2]) * inIncs[1] * data_type_size +
      (outExt[4] - inExt[4]) * inIncs[2] * data_type_size;

  // Get output pointer.
  outZPtr =
    static_cast<unsigned char*>(destIter->GetArray()->GetVoidPointer(0));

  // Loop over z axis.
  for (int zIdx = outExt[4]; zIdx <= outExt[5]; ++zIdx)
  {
    inPtr = inZPtr;
    outPtr = outZPtr;
    for (int yIdx = outExt[2]; yIdx <= outExt[3]; ++yIdx)
    {
      memcpy(outPtr, inPtr, rowLength * data_type_size);
      inPtr += inIncs[1] * data_type_size;
      outPtr += outIncs[1] * data_type_size;
    }
    inZPtr += inIncs[2] * data_type_size;
    outZPtr += outIncs[2] * data_type_size;
  }
}

//----------------------------------------------------------------------------
template<>
void vtkDataSetAttributesCopyValues(
  vtkArrayIteratorTemplate<vtkStdString>* destIter, const int* outExt,
  vtkIdType outIncs[3],
  vtkArrayIteratorTemplate<vtkStdString>* srcIter,
  const int* inExt, vtkIdType inIncs[3])
{
  vtkIdType inZIndex = (outExt[0] - inExt[0])*inIncs[0] +
    (outExt[2] - inExt[2])*inIncs[1] +
    (outExt[4] - inExt[4])*inIncs[2] ;

  vtkIdType outZIndex = 0;
  vtkIdType rowLength = outIncs[1];

  for (int zIdx = outExt[4]; zIdx <= outExt[5]; ++zIdx)
  {
    vtkIdType inIndex = inZIndex;
    vtkIdType outIndex = outZIndex;
    for (int yIdx = outExt[2]; yIdx <= outExt[3]; ++yIdx)
    {
      for (int xIdx = 0; xIdx < rowLength; ++xIdx)
      {
        destIter->GetValue(outIndex + xIdx) = srcIter->GetValue(inIndex + xIdx);
      }
      inIndex += inIncs[1];
      outIndex += outIncs[1];
    }
    inZIndex += inIncs[2];
    outZIndex += outIncs[2];
  }
}

//----------------------------------------------------------------------------
// This is used in the imaging pipeline for copying arrays.
// CopyAllocate needs to be called before this method.
void vtkDataSetAttributes::CopyStructuredData(vtkDataSetAttributes *fromPd,
                                              const int *inExt,
                                              const int *outExt,
                                              bool setSize)
{
  int i;

  for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
      i=this->RequiredArrays.NextIndex())
  {
    vtkAbstractArray *inArray = fromPd->Data[i];
    vtkAbstractArray *outArray = this->Data[this->TargetIndices[i]];
    vtkIdType inIncs[3];
    vtkIdType outIncs[3];
    vtkIdType zIdx;

    // Compute increments
    inIncs[0] = inArray->GetNumberOfComponents();
    inIncs[1] = inIncs[0] * (inExt[1]-inExt[0]+1);
    inIncs[2] = inIncs[1] * (inExt[3]-inExt[2]+1);
    outIncs[0] = inIncs[0];
    outIncs[1] = outIncs[0] * (outExt[1]-outExt[0]+1);
    outIncs[2] = outIncs[1] * (outExt[3]-outExt[2]+1);

    // Make sure the input extents match the actual array lengths.
    zIdx = inIncs[2]/inIncs[0]*(inExt[5]-inExt[4]+1);
    if (inArray->GetNumberOfTuples() != zIdx)
    {
      vtkErrorMacro("Input extent (" << inExt[0] << ", " << inExt[1] << ", "
                    << inExt[2] << ", " << inExt[3] << ", " << inExt[4] << ", "
                    << inExt[5] << ") does not match array length: " << zIdx);
      // Skip copying this array.
      continue;
    }
    // Make sure the output extents match the actual array lengths.
    zIdx = outIncs[2]/outIncs[0]*(outExt[5]-outExt[4]+1);
    if (outArray->GetNumberOfTuples() != zIdx && setSize)
    {
      // The "CopyAllocate" method only sets the size, not the number of tuples.
      outArray->SetNumberOfTuples(zIdx);
    }

    // We get very little performance improvement from this, but we'll leave the
    // legacy code around until we've done through benchmarking.
    vtkDataArray *inDA = vtkArrayDownCast<vtkDataArray>(inArray);
    vtkDataArray *outDA = vtkArrayDownCast<vtkDataArray>(outArray);
    if (!inDA || !outDA) // String array, etc
    {
      vtkArrayIterator* srcIter = inArray->NewIterator();
      vtkArrayIterator* destIter = outArray->NewIterator();
      switch (inArray->GetDataType())
      {
        vtkArrayIteratorTemplateMacro(
          vtkDataSetAttributesCopyValues(
            static_cast<VTK_TT*>(destIter), outExt, outIncs,
            static_cast<VTK_TT*>(srcIter), inExt, inIncs));
      }
      srcIter->Delete();
      destIter->Delete();
    }
    else
    {
      CopyStructuredDataWorker worker(outExt, inExt);
      if (!vtkArrayDispatch::Dispatch2SameValueType::Execute(outDA, inDA,
                                                             worker))
      {
        // Fallback to vtkDataArray API (e.g. vtkBitArray):
        worker(outDA, inDA);
      }
    }
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetupForCopy(vtkDataSetAttributes* pd)
{
  this->InternalCopyAllocate(pd, COPYTUPLE, 0, 0, false, false);
}


//--------------------------------------------------------------------------
// Allocates point data for point-by-point (or cell-by-cell) copy operation.
// If sze=0, then use the input DataSetAttributes to create (i.e., find
// initial size of) new objects; otherwise use the sze variable.
void vtkDataSetAttributes::InternalCopyAllocate(vtkDataSetAttributes* pd,
                                                int ctype,
                                                vtkIdType sze, vtkIdType ext,
                                                int shallowCopyArrays,
                                                bool createNewArrays)
{
  vtkAbstractArray* newAA;
  int i;

  // Create various point data depending upon input
  //
  if ( !pd )
  {
    return;
  }

  if ((ctype < COPYTUPLE) || (ctype > PASSDATA))
  {
    return;
  }

  this->RequiredArrays = this->ComputeRequiredArrays(pd, ctype);
  if (this->RequiredArrays.GetListSize() == 0)
  {
    return;
  }
  delete[] this->TargetIndices;
  this->TargetIndices = new int[pd->GetNumberOfArrays()];
  for(i=0; i<pd->GetNumberOfArrays(); i++)
  {
    this->TargetIndices[i] = -1;
  }

  vtkAbstractArray* aa=nullptr;
  // If we are not copying on self
  if ( (pd != this) && createNewArrays )
  {
    int attributeType;

    for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
        i=this->RequiredArrays.NextIndex())
    {
      // Create all required arrays
      aa = pd->GetAbstractArray(i);
      if (shallowCopyArrays)
      {
        newAA = aa;
      }
      else
      {
        newAA = aa->NewInstance();
        newAA->SetNumberOfComponents(aa->GetNumberOfComponents());
        newAA->CopyComponentNames( aa );
        newAA->SetName(aa->GetName());
        if (aa->HasInformation())
        {
          newAA->CopyInformation(aa->GetInformation(),/*deep=*/1);
        }
        if ( sze > 0 )
        {
          newAA->Allocate(sze*aa->GetNumberOfComponents(),ext);
        }
        else
        {
          newAA->Allocate(aa->GetNumberOfTuples());
        }
        vtkDataArray* newDA = vtkArrayDownCast<vtkDataArray>(newAA);
        if (newDA)
        {
          vtkDataArray* da = vtkArrayDownCast<vtkDataArray>(aa);
          newDA->SetLookupTable(da->GetLookupTable());
        }
      }
      this->TargetIndices[i] = this->AddArray(newAA);
      // If necessary, make the array an attribute
      if ( ((attributeType = pd->IsArrayAnAttribute(i)) != -1 ) &&
           this->CopyAttributeFlags[ctype][attributeType] )
      {
        this->CopyAttributeFlags[ctype][attributeType] =
          pd->CopyAttributeFlags[ctype][attributeType];
        this->SetActiveAttribute(this->TargetIndices[i], attributeType);
      }
      if (!shallowCopyArrays)
      {
        newAA->Delete();
      }
    }
  }
  else if (pd == this)
  {
    // If copying on self, resize the arrays and initialize
    // TargetIndices
    for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
        i=this->RequiredArrays.NextIndex())
    {
      aa = pd->GetAbstractArray(i);
      aa->Resize(sze);
      this->TargetIndices[i] = i;
    }
  }
  else
  {
    // All we are asked to do is create a mapping.
    // Here we assume that arrays are the same and ordered
    // the same way.
    for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
        i=this->RequiredArrays.NextIndex())
    {
      this->TargetIndices[i] = i;
    }
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::RemoveArray(int index)
{
  if ( (index<0) || (index>=this->NumberOfActiveArrays))
  {
    return;
  }
  this->Superclass::RemoveArray(index);
  int attributeType;
  for(attributeType = 0; attributeType < NUM_ATTRIBUTES; attributeType++)
  {
    if (this->AttributeIndices[attributeType] == index)
    {
      this->AttributeIndices[attributeType] = -1;
    }
    else if (this->AttributeIndices[attributeType] > index)
    {
      this->AttributeIndices[attributeType]--;
    }
  }
}

//--------------------------------------------------------------------------
// Copy the attribute data from one id to another. Make sure CopyAllocate() has
// been invoked before using this method.
void vtkDataSetAttributes::CopyData(vtkDataSetAttributes* fromPd,
                                    vtkIdType fromId, vtkIdType toId)
{
  int i;
  for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
      i=this->RequiredArrays.NextIndex())
  {
    this->CopyTuple(fromPd->Data[i], this->Data[this->TargetIndices[i]],
                    fromId, toId);
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyData(vtkDataSetAttributes *fromPd,
                                    vtkIdList *fromIds, vtkIdList *toIds)
{
  int i;
  for(i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
      i=this->RequiredArrays.NextIndex())
  {
    this->CopyTuples(fromPd->Data[i], this->Data[this->TargetIndices[i]],
        fromIds, toIds);
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyData(vtkDataSetAttributes *fromPd,
                                    vtkIdType dstStart, vtkIdType n,
                                    vtkIdType srcStart)
{
  for (int i = this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
       i = this->RequiredArrays.NextIndex())
  {
    this->CopyTuples(fromPd->Data[i], this->Data[this->TargetIndices[i]],
                     dstStart, n, srcStart);
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyAllocate(vtkDataSetAttributes* pd,
                                        vtkIdType sze, vtkIdType ext,
                                        int shallowCopyArrays)
{
  this->InternalCopyAllocate(pd, COPYTUPLE, sze, ext, shallowCopyArrays);
}

// Initialize point interpolation method.
void vtkDataSetAttributes::InterpolateAllocate(vtkDataSetAttributes* pd,
                                               vtkIdType sze, vtkIdType ext,
                                               int shallowCopyArrays)
{
  this->InternalCopyAllocate(pd, INTERPOLATE, sze, ext, shallowCopyArrays);
}

//--------------------------------------------------------------------------
// Interpolate data from points and interpolation weights. Make sure that the
// method InterpolateAllocate() has been invoked before using this method.
void vtkDataSetAttributes::InterpolatePoint(vtkDataSetAttributes *fromPd,
                                            vtkIdType toId, vtkIdList *ptIds,
                                            double *weights)
{
  for (int i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
       i=this->RequiredArrays.NextIndex())
  {
    vtkAbstractArray* fromArray = fromPd->Data[i];
    vtkAbstractArray* toArray = this->Data[this->TargetIndices[i]];

    //check if the destination array needs nearest neighbor interpolation
    int attributeIndex = this->IsArrayAnAttribute(this->TargetIndices[i]);
    if (attributeIndex != -1
        &&
        this->CopyAttributeFlags[INTERPOLATE][attributeIndex]==2)
    {
      vtkIdType numIds = ptIds->GetNumberOfIds();
      vtkIdType maxId = ptIds->GetId(0);
      vtkIdType maxWeight = 0.;
      for (int j=0;j<numIds;j++)
      {
        if (weights[j] > maxWeight)
        {
          maxWeight = weights[j];
          maxId = ptIds->GetId(j);
        }
      }
        toArray->InsertTuple(toId, maxId, fromArray);
    }
    else
    {
      toArray->InterpolateTuple(toId, ptIds, fromArray, weights);
    }
  }
}

//--------------------------------------------------------------------------
// Interpolate data from the two points p1,p2 (forming an edge) and an
// interpolation factor, t, along the edge. The weight ranges from (0,1),
// with t=0 located at p1. Make sure that the method InterpolateAllocate()
// has been invoked before using this method.
void vtkDataSetAttributes::InterpolateEdge(vtkDataSetAttributes *fromPd,
                                           vtkIdType toId, vtkIdType p1,
                                           vtkIdType p2, double t)
{
  for (int i=this->RequiredArrays.BeginIndex(); !this->RequiredArrays.End();
      i=this->RequiredArrays.NextIndex())
  {
    vtkAbstractArray* fromArray = fromPd->Data[i];
    vtkAbstractArray* toArray = this->Data[this->TargetIndices[i]];

    //check if the destination array needs nearest neighbor interpolation
    int attributeIndex = this->IsArrayAnAttribute(this->TargetIndices[i]);
    if (attributeIndex != -1
        &&
        this->CopyAttributeFlags[INTERPOLATE][attributeIndex]==2)
    {
      if (t < .5)
      {
        toArray->InsertTuple(toId, p1, fromArray);
      }
      else
      {
        toArray->InsertTuple(toId, p2, fromArray);
      }
    }
    else
    {
      toArray->InterpolateTuple(toId, p1, fromArray, p2, fromArray, t);
    }
  }
}

//--------------------------------------------------------------------------
// Interpolate data from the two points p1,p2 (forming an edge) and an
// interpolation factor, t, along the edge. The weight ranges from (0,1),
// with t=0 located at p1. Make sure that the method InterpolateAllocate()
// has been invoked before using this method.
void vtkDataSetAttributes::InterpolateTime(vtkDataSetAttributes *from1,
                                           vtkDataSetAttributes *from2,
                                           vtkIdType id, double t)
{
  for (int attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    // If this attribute is to be copied
    if (this->CopyAttributeFlags[INTERPOLATE][attributeType])
    {
      if (from1->GetAttribute(attributeType) &&
          from2->GetAttribute(attributeType))
      {
        vtkAbstractArray* toArray = this->GetAttribute(attributeType);
        //check if the destination array needs nearest neighbor interpolation
        if (this->CopyAttributeFlags[INTERPOLATE][attributeType]==2)
        {
          if (t < .5)
          {
            toArray->InsertTuple(id, id, from1->GetAttribute(attributeType));
          }
          else
          {
            toArray->InsertTuple(id, id, from2->GetAttribute(attributeType));
          }
        }
        else
        {
          toArray->InterpolateTuple(id, id, from1->GetAttribute(attributeType),
                                    id, from2->GetAttribute(attributeType), t);
        }
      }
    }
  }
}

//--------------------------------------------------------------------------
// Copy a tuple of data from one data array to another. This method (and
// following ones) assume that the fromData and toData objects are of the
// same type, and have the same number of components. This is true if you
// invoke CopyAllocate() or InterpolateAllocate().
void vtkDataSetAttributes::CopyTuple(vtkAbstractArray *fromData,
                                     vtkAbstractArray *toData, vtkIdType fromId,
                                     vtkIdType toId)
{
  toData->InsertTuple(toId, fromId, fromData);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyTuples(vtkAbstractArray *fromData,
                                      vtkAbstractArray *toData,
                                      vtkIdList *fromIds, vtkIdList *toIds)
{
  toData->InsertTuples(toIds, fromIds, fromData);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyTuples(vtkAbstractArray *fromData,
                                      vtkAbstractArray *toData,
                                      vtkIdType dstStart, vtkIdType n,
                                      vtkIdType srcStart)
{
  toData->InsertTuples(dstStart, n, srcStart, fromData);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetScalars(vtkDataArray* da)
{
  return this->SetAttribute(da, SCALARS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveScalars(const char* name)
{
  return this->SetActiveAttribute(name, SCALARS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveAttribute(const char* name,
                                             int attributeType)
{
  int index;
  this->GetAbstractArray(name, index);
  return this->SetActiveAttribute(index, attributeType);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetScalars()
{
  return this->GetAttribute(SCALARS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetVectors(vtkDataArray* da)
{
  return this->SetAttribute(da, VECTORS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveVectors(const char* name)
{
  return this->SetActiveAttribute(name, VECTORS);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetVectors()
{
  return this->GetAttribute(VECTORS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetNormals(vtkDataArray* da)
{
  return this->SetAttribute(da, NORMALS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveNormals(const char* name)
{
  return this->SetActiveAttribute(name, NORMALS);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetNormals()
{
  return this->GetAttribute(NORMALS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetTCoords(vtkDataArray* da)
{
  return this->SetAttribute(da, TCOORDS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveTCoords(const char* name)
{
  return this->SetActiveAttribute(name, TCOORDS);
}
//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetTCoords()
{
  return this->GetAttribute(TCOORDS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetTensors(vtkDataArray* da)
{
  return this->SetAttribute(da, TENSORS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveTensors(const char* name)
{
  return this->SetActiveAttribute(name, TENSORS);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetTensors()
{
  return this->GetAttribute(TENSORS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetGlobalIds(vtkDataArray* da)
{
  return this->SetAttribute(da, GLOBALIDS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveGlobalIds(const char* name)
{
  return this->SetActiveAttribute(name, GLOBALIDS);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetGlobalIds()
{
  return this->GetAttribute(GLOBALIDS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetPedigreeIds(vtkAbstractArray* aa)
{
  return this->SetAttribute(aa, PEDIGREEIDS);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActivePedigreeIds(const char* name)
{
  return this->SetActiveAttribute(name, PEDIGREEIDS);
}

//--------------------------------------------------------------------------
vtkAbstractArray* vtkDataSetAttributes::GetPedigreeIds()
{
  return this->GetAbstractAttribute(PEDIGREEIDS);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetScalars(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetScalars();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetVectors(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetVectors();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetNormals(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetNormals();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetTCoords(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetTCoords();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetTensors(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetTensors();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetGlobalIds(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetGlobalIds();
  }
  return this->GetArray(name);
}

//--------------------------------------------------------------------------
vtkAbstractArray* vtkDataSetAttributes::GetPedigreeIds(const char* name)
{
  if (name == nullptr || name[0] == '\0')
  {
    return this->GetPedigreeIds();
  }
  return this->GetAbstractArray(name);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::SetActiveAttribute(int index, int attributeType)
{
  if ( (index >= 0) && (index < this->GetNumberOfArrays()))
  {
    if (attributeType != PEDIGREEIDS)
    {
      vtkDataArray* darray = vtkArrayDownCast<vtkDataArray>(
        this->Data[index]);
      if (!darray)
      {
        vtkWarningMacro("Can not set attribute "
          << vtkDataSetAttributes::AttributeNames[attributeType]
          << ". Only vtkDataArray subclasses can be set as active attributes.");
        return -1;
      }
      if (!this->CheckNumberOfComponents(darray, attributeType))
      {
        vtkWarningMacro("Can not set attribute "
                        << vtkDataSetAttributes::AttributeNames[attributeType]
                        << ". Incorrect number of components.");
        return -1;
      }
    }

    this->AttributeIndices[attributeType] = index;
    this->Modified();
    return index;
  }
  else if (index == -1)
  {
    this->AttributeIndices[attributeType] = index;
    this->Modified();
  }

  return -1;
}

//--------------------------------------------------------------------------
const int vtkDataSetAttributes
::NumberOfAttributeComponents[vtkDataSetAttributes::NUM_ATTRIBUTES] =
{ 0,
  3,
  3,
  3,
  9,
  1,
  1,
  1};

//--------------------------------------------------------------------------
// Scalars set to NOLIMIT
const int vtkDataSetAttributes
::AttributeLimits[vtkDataSetAttributes::NUM_ATTRIBUTES] =
{ NOLIMIT,
  EXACT,
  EXACT,
  MAX,
  EXACT,
  EXACT,
  EXACT,
  EXACT};

//--------------------------------------------------------------------------
int vtkDataSetAttributes::CheckNumberOfComponents(vtkAbstractArray* aa,
                                                  int attributeType)
{
  int numComp = aa->GetNumberOfComponents();

  if ( vtkDataSetAttributes::AttributeLimits[attributeType] == MAX )
  {
    if ( numComp >
         vtkDataSetAttributes::NumberOfAttributeComponents[attributeType] )
    {
      return 0;
    }
    else
    {
      return 1;
    }
  }
  else if ( vtkDataSetAttributes::AttributeLimits[attributeType] == EXACT )
  {
    if (numComp ==
         vtkDataSetAttributes::NumberOfAttributeComponents[attributeType] ||
         (numComp == 6 && attributeType == TENSORS)) // TENSORS6 support
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  else if ( vtkDataSetAttributes::AttributeLimits[attributeType] == NOLIMIT )
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

//--------------------------------------------------------------------------
vtkDataArray* vtkDataSetAttributes::GetAttribute(int attributeType)
{
  int index = this->AttributeIndices[attributeType];
  if (index == -1)
  {
    return nullptr;
  }
  else
  {
    return vtkArrayDownCast<vtkDataArray>(this->Data[index]);
  }
}

//--------------------------------------------------------------------------
vtkAbstractArray* vtkDataSetAttributes::GetAbstractAttribute(int attributeType)
{
  int index = this->AttributeIndices[attributeType];
  if (index == -1)
  {
    return nullptr;
  }
  else
  {
    return this->Data[index];
  }
}

//--------------------------------------------------------------------------
// This method lets the user add an array and make it the current
// scalars, vectors etc... (this is determined by the attribute type
// which is an enum defined vtkDataSetAttributes)
int vtkDataSetAttributes::SetAttribute(vtkAbstractArray* aa, int attributeType)
{
  if (aa && attributeType != PEDIGREEIDS && !vtkArrayDownCast<vtkDataArray>(aa))
  {
    vtkWarningMacro("Can not set attribute "
                    << vtkDataSetAttributes::AttributeNames[attributeType]
                    << ". This attribute must be a subclass of vtkDataArray.");
    return -1;
  }
  if (aa && !this->CheckNumberOfComponents(aa, attributeType))
  {
    vtkWarningMacro("Can not set attribute "
                    << vtkDataSetAttributes::AttributeNames[attributeType]
                    << ". Incorrect number of components.");
    return -1;
  }

  int currentAttribute = this->AttributeIndices[attributeType];

  // If there is an existing attribute, replace it
  if ( (currentAttribute >= 0) &&
       (currentAttribute < this->GetNumberOfArrays()) )
  {
    if (this->GetAbstractArray(currentAttribute) == aa)
    {
      return currentAttribute;
    }
    this->RemoveArray(currentAttribute);
  }

  if (aa)
  {
    // Add the array
    currentAttribute = this->AddArray(aa);
    this->AttributeIndices[attributeType] = currentAttribute;
  }
  else
  {
    this->AttributeIndices[attributeType] = -1; //attribute of this type doesn't exist
  }
  this->Modified();
  return this->AttributeIndices[attributeType];
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  // Print the copy flags
  int i;
  os << indent << "Copy Tuple Flags: ( ";
  for (i=0; i<NUM_ATTRIBUTES; i++)
  {
    os << this->CopyAttributeFlags[COPYTUPLE][i] << " ";
  }
  os << ")" << endl;
  os << indent << "Interpolate Flags: ( ";
  for (i=0; i<NUM_ATTRIBUTES; i++)
  {
    os << this->CopyAttributeFlags[INTERPOLATE][i] << " ";
  }
  os << ")" << endl;
  os << indent << "Pass Through Flags: ( ";
  for (i=0; i<NUM_ATTRIBUTES; i++)
  {
    os << this->CopyAttributeFlags[PASSDATA][i] << " ";
  }
  os << ")" << endl;

  // Now print the various attributes
  vtkAbstractArray* aa;
  int attributeType;
  for (attributeType=0; attributeType<NUM_ATTRIBUTES; attributeType++)
  {
    os << indent << vtkDataSetAttributes::AttributeNames[attributeType]
       << ": ";
    if ( (aa=this->GetAbstractAttribute(attributeType)) )
    {
      os << endl;
      aa->PrintSelf(os, indent.GetNextIndent());
    }
    else
    {
      os << "(none)" << endl;
    }
  }

}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::GetAttributeIndices(int* indexArray)
{
  int i;
  for(i=0; i<NUM_ATTRIBUTES; i++)
  {
    indexArray[i] = this->AttributeIndices[i];
  }
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::IsArrayAnAttribute(int idx)
{
  int i;
  for (i=0; i<NUM_ATTRIBUTES; i++)
  {
    if ( idx == this->AttributeIndices[i] )
    {
      return i;
    }
  }
  return -1;
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyAttribute (int index, int value, int ctype)
{
  if (index < 0 || ctype < 0 || index >= vtkDataSetAttributes::NUM_ATTRIBUTES ||
      ctype > vtkDataSetAttributes::ALLCOPY)
  {
    vtkErrorMacro("Cannot set copy attribute for attribute type "
                  << index << " and copy operation " << ctype
                  << ". These values are out of range.");
    return;
  }

  if (ctype == vtkDataSetAttributes::ALLCOPY)
  {
    int t;
    for (t = COPYTUPLE; t < vtkDataSetAttributes::ALLCOPY; t++)
    {
      if (this->CopyAttributeFlags[t][ index ] != value)
      {
        this->CopyAttributeFlags[t][ index ] = value;
        this->Modified();
      }
    }
  }
  else
  {
    if (this->CopyAttributeFlags[ctype][ index ] != value)
    {
      this->CopyAttributeFlags[ctype][ index ] = value;
      this->Modified();
    }
  }
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyAttribute (int index, int ctype)
{
  if (index < 0 || ctype < 0 || index >= vtkDataSetAttributes::NUM_ATTRIBUTES ||
      ctype > vtkDataSetAttributes::ALLCOPY)
  {
    vtkWarningMacro("Cannot get copy attribute for attribute type "
                    << index << " and copy operation " << ctype
                    << ". These values are out of range.");
    return -1;
  }
  else if (ctype == vtkDataSetAttributes::ALLCOPY)
  {
    return (this->CopyAttributeFlags[COPYTUPLE][index] &&
            this->CopyAttributeFlags[INTERPOLATE][index] &&
            this->CopyAttributeFlags[PASSDATA][index]);
  }
  else
  {
    return this->CopyAttributeFlags[ctype][index];
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyScalars(int i, int ctype)
{
  this->SetCopyAttribute(SCALARS, i, ctype);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyScalars(int ctype)
{
  return this->GetCopyAttribute(SCALARS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyVectors(int i, int ctype)
{
  this->SetCopyAttribute(VECTORS, i, ctype);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyVectors(int ctype)
{
  return this->GetCopyAttribute(VECTORS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyNormals(int i, int ctype)
{
  this->SetCopyAttribute(NORMALS, i, ctype);
}
//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyNormals(int ctype)
{
  return this->GetCopyAttribute(NORMALS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyTCoords(int i, int ctype)
{
  this->SetCopyAttribute(TCOORDS, i, ctype);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyTCoords(int ctype)
{
  return this->GetCopyAttribute(TCOORDS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyTensors(int i, int ctype)
{
  this->SetCopyAttribute(TENSORS, i, ctype);
}
//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyTensors(int ctype)
{
  return this->GetCopyAttribute(TENSORS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyGlobalIds(int i, int ctype)
{
  this->SetCopyAttribute(GLOBALIDS, i, ctype);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyGlobalIds(int ctype)
{
  return this->GetCopyAttribute(GLOBALIDS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::SetCopyPedigreeIds(int i, int ctype)
{
  this->SetCopyAttribute(PEDIGREEIDS, i, ctype);
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::GetCopyPedigreeIds(int ctype)
{
  return this->GetCopyAttribute(PEDIGREEIDS, ctype);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::CopyAllocate(
  vtkDataSetAttributes::FieldList& list,
  vtkIdType sze, vtkIdType ext)
{
  this->InternalCopyAllocate(list, COPYTUPLE, sze, ext);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::InterpolateAllocate(
  vtkDataSetAttributes::FieldList& list, vtkIdType sze,
  vtkIdType ext)
{
  this->InternalCopyAllocate(list, INTERPOLATE, sze, ext);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::InternalCopyAllocate(
  vtkDataSetAttributes::FieldList& list,
  int ctype,
  vtkIdType sze, vtkIdType ext)
{
  vtkAbstractArray* newAA=nullptr;
  vtkDataArray* newDA=nullptr;
  int i;

  // Allocate attributes if any
  for (i=0; i < list.NumberOfFields; i++)
  {
    if ( list.FieldIndices[i] >= 0 )
    {
      newAA = vtkAbstractArray::CreateArray(list.FieldTypes[i]);
      newAA->SetName(list.Fields[i]);
      newAA->SetNumberOfComponents(list.FieldComponents[i]);

      if ( list.FieldComponentsNames[i] )
      {
        for (unsigned int j=0; j < list.FieldComponentsNames[i]->size(); ++j)
        {
          if (list.FieldComponentsNames[i]->at(j).first)
          {
            newAA->SetComponentName(j,
              list.FieldComponentsNames[i]->at(j).second.c_str());
          }
        }
      }
      if (list.FieldInformation[i])
      {
        newAA->CopyInformation(list.FieldInformation[i],/*deep=*/1);
      }

      if ( sze > 0 )
      {
        newAA->Allocate(sze,ext);
      }
      else
      {
        newAA->Allocate(list.NumberOfTuples,ext);
      }

      if ( (newDA = vtkArrayDownCast<vtkDataArray>(newAA)) )
      {
        newDA->SetLookupTable(list.LUT[i]);
      }

      // If attribute data, do something extra
      if ( i < NUM_ATTRIBUTES )
      {
        // since attributes can only be DataArray, newDA must be non-null.
        if ( this->CopyAttributeFlags[ctype][i] && newDA)
        {
          list.FieldIndices[i] = this->AddArray(newDA);
          this->SetActiveAttribute(list.FieldIndices[i], i);
        }
        else
        {
          list.FieldIndices[i] = -1;
        }
      }
      else //check if this field is to be copied
      {
        if ( (this->GetFlag(list.Fields[i]) != 0) &&
             !(this->DoCopyAllOff && (this->GetFlag(list.Fields[i]) != 1)) )
        {
          list.FieldIndices[i] = this->AddArray(newAA);
        }
        else
        {
          list.FieldIndices[i] = -1;
        }
      }

      newAA->Delete(); //okay, reference counting
    }//data array defined
  }
}

//--------------------------------------------------------------------------
// Description:
// A special form of CopyData() to be used with FieldLists. Use it when you are
// copying data from a set of vtkDataSetAttributes. Make sure that you have
// called the special form of CopyAllocate that accepts FieldLists.
void vtkDataSetAttributes::CopyData(vtkDataSetAttributes::FieldList& list,
                                    vtkDataSetAttributes* fromDSA,
                                    int idx, vtkIdType fromId, vtkIdType toId)
{

  for (int i=0; i < list.NumberOfFields; i++)
  {
    if ( list.FieldIndices[i] >= 0 && list.DSAIndices[idx][i] >= 0 )
    {
      vtkAbstractArray *toDA = this->GetAbstractArray(list.FieldIndices[i]);
      vtkAbstractArray *fromDA = fromDSA->GetAbstractArray(list.DSAIndices[idx][i]);
      this->CopyTuple(fromDA, toDA, fromId, toId);
    }
  }
}

//--------------------------------------------------------------------------
// Description:
// A special form of CopyData() to be used with FieldLists. Use it when you are
// copying data from a set of vtkDataSetAttributes. Make sure that you have
// called the special form of CopyAllocate that accepts FieldLists.
void vtkDataSetAttributes::CopyData(vtkDataSetAttributes::FieldList& list,
                                    vtkDataSetAttributes* fromDSA,
                                    int idx, vtkIdType dstStart, vtkIdType n,
                                    vtkIdType srcStart)
{
  for (int i=0; i < list.NumberOfFields; i++)
  {
    if ( list.FieldIndices[i] >= 0 && list.DSAIndices[idx][i] >= 0 )
    {
      vtkAbstractArray *toDA = this->GetAbstractArray(list.FieldIndices[i]);
      vtkAbstractArray *fromDA = fromDSA->GetAbstractArray(list.DSAIndices[idx][i]);
      this->CopyTuples(fromDA, toDA, dstStart, n, srcStart);
    }
  }
}

//--------------------------------------------------------------------------
// Interpolate data from points and interpolation weights. Make sure that the
// method InterpolateAllocate() has been invoked before using this method.
void vtkDataSetAttributes::InterpolatePoint(
  vtkDataSetAttributes::FieldList& list,
  vtkDataSetAttributes *fromPd,
  int idx,
  vtkIdType toId, vtkIdList *ptIds,
  double *weights)
{
  vtkAbstractArray *fromArray;
  vtkAbstractArray *toArray;

  for (int i=0; i < list.NumberOfFields; i++)
  {
    if ( list.FieldIndices[i] >= 0 && list.DSAIndices[idx][i] >= 0 )
    {
      toArray = this->GetAbstractArray(list.FieldIndices[i]);
      fromArray = fromPd->GetAbstractArray(list.DSAIndices[idx][i]);

      //check if the destination array needs nearest neighbor interpolation
      int attributeIndex = this->IsArrayAnAttribute(list.DSAIndices[idx][i]);
      if (attributeIndex != -1
          &&
          this->CopyAttributeFlags[INTERPOLATE][attributeIndex]==2)
      {
        vtkIdType numIds = ptIds->GetNumberOfIds();
        vtkIdType maxId = ptIds->GetId(0);
        vtkIdType maxWeight = 0.;
        for (int j=0;j<numIds;j++)
        {
          if (weights[j] > maxWeight)
          {
            maxWeight = weights[j];
            maxId = ptIds->GetId(j);
          }
        }
        toArray->InsertTuple(toId, maxId, fromArray);
      }
      else
      {
        toArray->InterpolateTuple(toId, ptIds, fromArray, weights);
      }
    }
  }
}

//--------------------------------------------------------------------------
const char* vtkDataSetAttributes::GetAttributeTypeAsString(int attributeType)
{
  if (attributeType < 0 || attributeType >= NUM_ATTRIBUTES)
  {
    vtkGenericWarningMacro("Bad attribute type: " << attributeType << ".");
    return nullptr;
  }
  return vtkDataSetAttributes::AttributeNames[attributeType];
}

//--------------------------------------------------------------------------
const char* vtkDataSetAttributes::GetLongAttributeTypeAsString(int attributeType)
{
  if (attributeType < 0 || attributeType >= NUM_ATTRIBUTES)
  {
    vtkGenericWarningMacro("Bad attribute type: " << attributeType << ".");
    return nullptr;
  }
  return vtkDataSetAttributes::LongAttributeNames[attributeType];
}

//=============================================================================
vtkDataSetAttributes::FieldList::FieldList(int numInputs)
{
  this->Fields = nullptr;
  this->FieldTypes = nullptr;
  this->FieldComponents = nullptr;
  this->FieldComponentsNames = nullptr;
  this->FieldIndices = nullptr;
  this->NumberOfFields = 0;
  this->LUT = nullptr;
  this->FieldInformation = nullptr;
  this->DSAIndices = nullptr;
  this->NumberOfDSAIndices = 0;
  //
  if (numInputs)
  {
    this->NumberOfDSAIndices = numInputs;
    this->DSAIndices = new int*[numInputs];
    int i;
    for (i=0; i<numInputs; i++)
    {
      this->DSAIndices[i] = nullptr;
    }
  }
}

//--------------------------------------------------------------------------
vtkDataSetAttributes::FieldList::~FieldList()
{
  this->ClearFields();
  delete [] this->DSAIndices;
  this->DSAIndices = nullptr;
}

//----------------------------------------------------------------------------
// To perform intersection of attribute data, use InitializeFieldList() to grab
// an initial vtkDataSetAttributes. Then use IntersectFieldList() to add (and
// intersect) additional vtkDataSetAttributes.
void vtkDataSetAttributes::FieldList::InitializeFieldList(
  vtkDataSetAttributes* dsa)
{
  int i;
  this->ClearFields();
  // Allocate space for the arrays plus five attributes
  this->NumberOfFields = dsa->GetNumberOfArrays() + NUM_ATTRIBUTES;
  this->Fields = new char*[this->NumberOfFields];
  this->FieldTypes = new int [this->NumberOfFields];
  this->FieldComponents = new int [this->NumberOfFields];
  this->FieldComponentsNames =
    new vtkDataSetAttributes::vtkInternalComponentNames*[this->NumberOfFields];
  this->FieldIndices = new int [this->NumberOfFields];
  this->LUT = new vtkLookupTable* [this->NumberOfFields];
  this->FieldInformation = new vtkInformation* [this->NumberOfFields];
  for(i=0; i < this->NumberOfFields; i++)
  {
    this->Fields[i] = nullptr;
    this->FieldTypes[i] = -1;
    this->FieldComponents[i] = 0;
    this->FieldComponentsNames[i] = nullptr;
    this->FieldIndices[i] = -1;
    this->LUT[i] = nullptr;
    this->FieldInformation[i] = nullptr;
  }
  this->CurrentInput = 0;
  this->NumberOfTuples = 0;

  //there may be no data hence dsa->Data
  for(i=0; dsa->Data && i < dsa->GetNumberOfArrays(); i++)
  {
    int attrType = dsa->IsArrayAnAttribute(i);
    if ( attrType != -1 ) //it's an attribute
    {
      this->FieldIndices[attrType] = i;
      this->SetField(attrType, dsa->Data[i]);
    }
    else
    {
      this->FieldIndices[NUM_ATTRIBUTES+i] = i;
      this->SetField(NUM_ATTRIBUTES+i, dsa->Data[i]);
    }
  }

  // The first dataset is added to the field list
  this->IntersectFieldList(dsa);
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::UnionFieldList(vtkDataSetAttributes* dsa)
{
  vtkAbstractArray* aa;
  vtkDataArray* da;
  // Keep a running total of the number of tuples...might be useful
  // for later allocation.
  if ( (aa=dsa->GetAbstractArray(0)) )
  {
    this->NumberOfTuples += aa->GetNumberOfTuples();
  }

  // unlike Intersection, with Union the the total number of fields may change,
  // so we have to be careful with that.
  std::vector<int> dsaIndices;
  dsaIndices.resize(this->NumberOfFields, -1);

  // Intersect the active attributes. (Even though we are taking a union, we
  // intersect the active attributes).
  int attributeIndices[NUM_ATTRIBUTES];
  dsa->GetAttributeIndices(attributeIndices);
  for (int i=0; i < NUM_ATTRIBUTES; i++)
  {
    if (this->FieldIndices[i] >= 0)
    {
      da = dsa->GetAttribute(i);
      if ((da) && (da->GetDataType() == this->FieldTypes[i]) &&
          (da->GetNumberOfComponents() == this->FieldComponents[i]))
      {
        dsaIndices[i] = attributeIndices[i];
      }
      else
      {
        // doh! A attribute is not available in this new dsa. But it was
        // available until now.

        // In InitializeFieldList(), if an array is an active attribute, its
        // information is noted only in the first "set". Now since we are
        // marking it as not-an-attribute, we still don't want to lose the
        // array. So we enable it in the second "set". But all DSAIndices until
        // the CurrentInput referred to this array in it's location in the
        // first "set" so we have to move those as well. That's what's
        // happening here.
        int offset = this->FieldIndices[i];
        this->FieldIndices[NUM_ATTRIBUTES + offset] = this->FieldIndices[i];
        this->Fields[offset+NUM_ATTRIBUTES] = this->Fields[i];
        this->FieldTypes[offset+NUM_ATTRIBUTES] = this->FieldTypes[i];
        this->FieldComponents[offset+NUM_ATTRIBUTES] = this->FieldComponents[i];
        this->FieldComponentsNames[offset+NUM_ATTRIBUTES] = this->FieldComponentsNames[i];
        this->LUT[offset+NUM_ATTRIBUTES] = this->LUT[i];
        this->FieldInformation[offset+NUM_ATTRIBUTES] = this->FieldInformation[i];

        this->FieldIndices[i] = -1; //Attribute not present
        this->Fields[i] = nullptr;
        this->FieldTypes[i] = -1;
        this->FieldComponents[i] = 0;
        this->FieldComponentsNames[i] = nullptr;
        this->LUT[i] = nullptr;
        this->FieldInformation[i] = nullptr;

        for (int cc=0; cc < this->CurrentInput && cc < this->NumberOfDSAIndices;
          cc++)
        {
          this->DSAIndices[cc][offset+NUM_ATTRIBUTES] = this->DSAIndices[cc][i];
          this->DSAIndices[cc][i] = -1;
        }
      }
    }
  }

  std::vector<bool> dsaMarkedArrays;
  dsaMarkedArrays.resize(dsa->GetNumberOfArrays(), false);

  // * Try to match the existing fields with those in dsa.
  for (int i = NUM_ATTRIBUTES; i < this->NumberOfFields; i++)
  {
    // FieldIndices should really be a bool.
    if (this->FieldIndices[i] < 0)
    {
      continue;
    }
    int index;
    aa = dsa->GetAbstractArray(this->Fields[i], index);
    if ((aa) && (aa->GetDataType() == this->FieldTypes[i]) &&
      (aa->GetNumberOfComponents() == this->FieldComponents[i]))
    {
      dsaIndices[i] = index;
      dsaMarkedArrays[index] = true;
    }
  }

  // * Now every array in dsaMarkedArrays that has a false, implies that it did not
  // match with any of the existing fields. So those will be appended to the
  // end of the field list.
  std::vector<int> dsaPendingIndices;
  for (size_t cc=0; cc < dsaMarkedArrays.size(); cc++)
  {
    if (dsaMarkedArrays[cc] == false)
    {
      dsaPendingIndices.push_back(static_cast<int>(cc));
    }
  }

  if (!dsaPendingIndices.empty())
  {
    size_t old_size = dsaIndices.size();
    size_t new_size = old_size + dsaPendingIndices.size();

    // * If dsaPendingIndices != empty, then we need to grow the num of fields.
    this->GrowBy(static_cast<unsigned int>(dsaPendingIndices.size()));

    dsaIndices.resize(new_size, -1);
    for (size_t cc=0; cc < dsaPendingIndices.size(); cc++)
    {
      this->FieldIndices[old_size+cc] =
        static_cast<int>((old_size+cc) - NUM_ATTRIBUTES);
      this->SetField(static_cast<int>(old_size+cc),
        dsa->GetAbstractArray(dsaPendingIndices[cc]));
      dsaIndices[old_size + cc] = dsaPendingIndices[cc];
    }
  }

  this->DSAIndices[this->CurrentInput] = new int [this->NumberOfFields];
  memcpy(this->DSAIndices[this->CurrentInput], &dsaIndices[0],
    sizeof(int)*this->NumberOfFields);

  this->CurrentInput++;
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::GrowBy(unsigned int delta)
{
  if (delta == 0)
  {
    return;
  }

  int old_size = this->NumberOfFields;
  int new_size = this->NumberOfFields + delta;

  char** newFields = new char*[new_size];
  int* newFieldTypes = new int[new_size];
  int* newFieldComponents = new int [new_size];
  vtkDataSetAttributes::vtkInternalComponentNames** newFieldComponentsNames
    = new vtkDataSetAttributes::vtkInternalComponentNames* [ new_size ];

  int* newFieldIndices = new int [new_size];
  vtkLookupTable** newLUT = new vtkLookupTable* [new_size];
  vtkInformation** newFieldInformation = new vtkInformation* [new_size];

  // copy the old fields.
  for(int i=0; i < old_size; i++)
  {
    if (this->Fields[i])
    {
      newFields[i] = strdup(this->Fields[i]);
    }
    else
    {
      newFields[i] = nullptr;
    }
    if ( this->FieldComponentsNames[i] )
    {
      newFieldComponentsNames[i] =
        new vtkDataSetAttributes::vtkInternalComponentNames(
        *this->FieldComponentsNames[i]);
    }
    else
    {
      newFieldComponentsNames[i] = nullptr;
    }
  }
  memcpy(newFieldTypes, this->FieldTypes, sizeof(int)*old_size);
  memcpy(newFieldComponents, this->FieldComponents, sizeof(int)*old_size);
  memcpy(newFieldIndices, this->FieldIndices, sizeof(int)*old_size);
  memcpy(newLUT, this->LUT, sizeof(vtkLookupTable*)*old_size);
  memcpy(newFieldInformation, this->FieldInformation,
    sizeof(vtkInformation*)*old_size);

  // initialize the rest.
  for (int i=old_size; i < new_size; i++)
  {
    newFields[i] = nullptr;
    newFieldTypes[i] = -1;
    newFieldComponents[i] = 0;
    newFieldIndices[i] = -1;
    newLUT[i] = nullptr;
    newFieldInformation[i] = nullptr;
    newFieldComponentsNames[i] = nullptr;
  }

  int **newDSAIndices = new int*[this->NumberOfDSAIndices];
  for (int cc=0; cc < this->NumberOfDSAIndices; cc++)
  {
    if (this->DSAIndices[cc] != nullptr)
    {
      newDSAIndices[cc] = new int[new_size];
      memcpy(newDSAIndices[cc], this->DSAIndices[cc], sizeof(int)*old_size);
      for (int kk=old_size; kk < new_size; kk++)
      {
        newDSAIndices[cc][kk] = -1;
      }
    }
    else
    {
      newDSAIndices[cc] = nullptr;
    }
  }

  int currentInput = this->CurrentInput;
  int numberOfDSAIndices = this->NumberOfDSAIndices;

  this->ClearFields();

  this->NumberOfFields = new_size;
  this->NumberOfDSAIndices = numberOfDSAIndices;
  this->CurrentInput = currentInput;
  this->Fields = newFields;
  this->FieldTypes = newFieldTypes;
  this->FieldComponents = newFieldComponents;
  this->FieldComponentsNames = newFieldComponentsNames;
  this->FieldIndices = newFieldIndices;
  this->LUT = newLUT;
  this->FieldInformation = newFieldInformation;
  this->DSAIndices = newDSAIndices;
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::IntersectFieldList(vtkDataSetAttributes* dsa)
{
  int i;
  vtkDataArray *da;
  vtkAbstractArray* aa;

  // Initialize the indices for this dataset
  this->DSAIndices[this->CurrentInput] = new int [this->NumberOfFields];
  for (i=0; i < this->NumberOfFields; i++)
  {
    this->DSAIndices[this->CurrentInput][i]= -1;
  }

  // Keep a running total of the number of tuples...might be useful
  // for later allocation.
  if ( (da=dsa->GetArray(0)) )
  {
    this->NumberOfTuples += da->GetNumberOfTuples();
  }

  // Intersect the attributes
  int attributeIndices[NUM_ATTRIBUTES];
  dsa->GetAttributeIndices(attributeIndices);
  for(i=0; i < NUM_ATTRIBUTES; i++)
  {
    if ( this->FieldIndices[i] >= 0 )
    {
      da = dsa->GetAttribute(i);
      if ((da) && (da->GetDataType() == this->FieldTypes[i]) &&
          (da->GetNumberOfComponents() == this->FieldComponents[i]))
      {
        this->DSAIndices[this->CurrentInput][i] = attributeIndices[i];
      }
      else
      {
        this->FieldIndices[i] = -1; //Attribute not present
      }
    }
  }
  // Intersect the fields
  int index;
  for(i=NUM_ATTRIBUTES; i < this->NumberOfFields; i++)
  {
    if (this->FieldIndices[i] >= 0)
    {
      aa = dsa->GetAbstractArray(this->Fields[i], index);
      if ((aa) && (aa->GetDataType() == this->FieldTypes[i]) &&
          (aa->GetNumberOfComponents() == this->FieldComponents[i]))
      {
        this->DSAIndices[this->CurrentInput][i] = index;
      }
      else
      {
        this->FieldIndices[i] = -1; //Field not present
      }
    }
  }

  this->CurrentInput++;
}

//--------------------------------------------------------------------------
int vtkDataSetAttributes::FieldList::IsAttributePresent(int attrType)
{
  return this->FieldIndices[attrType];
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::ClearFields()
{
  int i;
  if ( this->Fields )
  {
    for (i=0; i<this->NumberOfFields; i++)
    {
      delete [] this->Fields[i];
      this->Fields[i] = nullptr;
    }
  }
  if ( this->DSAIndices )
  {
    for (i=0; i<this->NumberOfDSAIndices; i++)
    {
      delete[] this->DSAIndices[i];
      this->DSAIndices[i] = nullptr;
    }
  }
  //
  delete [] this->Fields;
  this->Fields = nullptr;

  delete [] this->FieldInformation;
  this->FieldInformation = nullptr;

  delete [] this->LUT;
  this->LUT = nullptr;

  delete [] this->FieldTypes;
  this->FieldTypes = nullptr;

  delete [] this->FieldComponents;
  this->FieldComponents = nullptr;

  if ( this->FieldComponentsNames )
  {
    for (i=0; i<this->NumberOfFields; i++)
    {
      delete this->FieldComponentsNames[i];
    }
    delete [] this->FieldComponentsNames;
    this->FieldComponentsNames = nullptr;
  }

  delete [] this->FieldIndices;
  this->FieldIndices = nullptr;

  this->NumberOfFields = 0;
  this->CurrentInput = 0;
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::SetField(
        int index,
        vtkAbstractArray *aa)
{
  // Store the field name
  delete [] this->Fields[index];
  this->Fields[index] = nullptr;
  const char* name=aa->GetName();
  if (name)
  {
    int len = static_cast<int>(strlen(name));
    if (len > 0)
    {
      this->Fields[index] = new char[len+1];
      strcpy(this->Fields[index], name);
    }
  }
  // Store the data type
  this->FieldTypes[index] = aa->GetDataType();

  //we unallocate the names before we update the field components
  //so we unallocate correctly
  delete this->FieldComponentsNames[index];
  this->FieldComponentsNames[index] = nullptr;

  //store the components names
  int numberOfComponents = aa->GetNumberOfComponents();
  if ( aa->HasAComponentName() )
  {
    this->FieldComponentsNames[index] =
      new vtkDataSetAttributes::vtkInternalComponentNames();
    this->FieldComponentsNames[index]->resize(numberOfComponents,
      std::pair<bool, vtkStdString>(false, vtkStdString()));
    name = nullptr;
    for ( vtkIdType i=0; i < numberOfComponents; ++i)
    {
      name = aa->GetComponentName(i);
      if ( name )
      {
        this->FieldComponentsNames[index]->at(i) =
          std::pair<bool, vtkStdString>(true, name);
        name = nullptr;
      }
    }
  }

  // Store the components
  this->FieldComponents[index] = numberOfComponents;

  // Store the lookup table
  this->LUT[index]=nullptr;
  if (vtkArrayDownCast<vtkDataArray>(aa))
  {
    this->LUT[index]= vtkArrayDownCast<vtkDataArray>(aa)->GetLookupTable();
  }
  // Store the information
  this->FieldInformation[index] = nullptr;
  if (aa->HasInformation())
  {
    this->FieldInformation[index] = aa->GetInformation();
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::RemoveField(const char *name)
{
  if ( !name )
  {
    return;
  }

  int i;
  for (i=NUM_ATTRIBUTES; i < this->NumberOfFields; i++)
  {
    if ( this->Fields[i] && !strcmp(this->Fields[i],name) )
    {
      delete [] this->Fields[i];
      this->Fields[i] = nullptr;
      this->FieldTypes[i] = -1;
      this->FieldComponents[i] = 0;

      delete this->FieldComponentsNames[i];
      this->FieldComponentsNames[i] = nullptr;

      this->FieldIndices[i] = -1;
      this->LUT[i] = nullptr;
      this->FieldInformation[i] = nullptr;
      return;
    }
  }
}

//--------------------------------------------------------------------------
void vtkDataSetAttributes::FieldList::PrintSelf(ostream &os, vtkIndent indent)
{
  os << indent << "Number of Fields:" << this->NumberOfFields << endl;
  vtkIndent nextIndent=indent.GetNextIndent();
  for (int i=0; i<this->NumberOfFields; ++i)
  {
    os << indent << "Field " << i << " {" << endl
       << nextIndent
       << (this->Fields[i]==nullptr?"nullptr":this->Fields[i]) << ", "
       << this->FieldTypes[i] << ", "
       << this->FieldComponents[i] << ", "
       << this->FieldIndices[i] << ", "
       << this->FieldInformation[i]
       << "}" << endl;
  }
}
