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

  Program:   Visualization Toolkit
  Module:    vtkArrayListTemplate.txx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See LICENSE file 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 "vtkArrayListTemplate.h"
#include "vtkFloatArray.h"

#include <cassert>

#ifndef vtkArrayListTemplate_txx
#define vtkArrayListTemplate_txx

//----------------------------------------------------------------------------
// Sort of a little object factory (in conjunction w/ vtkTemplateMacro())
template <typename T>
void CreateArrayPair(ArrayList *list, T *inData, T *outData,
                     vtkIdType numTuples, int numComp, vtkDataArray *outArray,
                     T nullValue)
{
  ArrayPair<T> *pair = new ArrayPair<T>(inData,outData,numTuples,numComp,
                                        outArray,nullValue);
  list->Arrays.push_back(pair);
}

//----------------------------------------------------------------------------
// Sort of a little object factory (in conjunction w/ vtkTemplateMacro())
template <typename T>
void CreateRealArrayPair(ArrayList *list, T *inData, float *outData,
                         vtkIdType numTuples, int numComp, vtkDataArray *outArray,
                         float nullValue)
{
  RealArrayPair<T,float> *pair =
    new RealArrayPair<T,float>(inData,outData,numTuples,numComp,
                               outArray,nullValue);
  list->Arrays.push_back(pair);
}

//----------------------------------------------------------------------------
// Indicate arrays not to process
inline void ArrayList::
ExcludeArray(vtkDataArray *da)
{
  ExcludedArrays.push_back(da);
}

//----------------------------------------------------------------------------
// Has the specified array been excluded?
inline bool ArrayList::
IsExcluded(vtkDataArray *da)
{
  return (std::find(ExcludedArrays.begin(), ExcludedArrays.end(), da) != ExcludedArrays.end());
}

//----------------------------------------------------------------------------
// Add an array pair (input,output) using the name provided for the output. The
// numTuples is the number of output tuples allocated.
inline vtkDataArray* ArrayList::
AddArrayPair(vtkIdType numTuples, vtkDataArray *inArray,
             vtkStdString &outArrayName, double nullValue, bool promote)
{
  if (this->IsExcluded(inArray))
  {
    return nullptr;
  }

  int iType = inArray->GetDataType();
  vtkDataArray *outArray;
  if ( promote && iType != VTK_FLOAT && iType != VTK_DOUBLE )
  {
    outArray = vtkFloatArray::New();
    outArray->SetNumberOfComponents(inArray->GetNumberOfComponents());
    outArray->SetNumberOfTuples(numTuples);
    outArray->SetName(outArrayName);
    void *iD = inArray->GetVoidPointer(0);
    void *oD = outArray->GetVoidPointer(0);
    switch (iType)
    {
      vtkTemplateMacro(CreateRealArrayPair(this, static_cast<VTK_TT *>(iD),
                       static_cast<float*>(oD),numTuples,inArray->GetNumberOfComponents(),
                       outArray,static_cast<float>(nullValue)));
    }//over all VTK types
  }
  else
  {
    outArray = inArray->NewInstance();
    outArray->SetNumberOfComponents(inArray->GetNumberOfComponents());
    outArray->SetNumberOfTuples(numTuples);
    outArray->SetName(outArrayName);
    void *iD = inArray->GetVoidPointer(0);
    void *oD = outArray->GetVoidPointer(0);
    switch (iType)
    {
      vtkTemplateMacro(CreateArrayPair(this, static_cast<VTK_TT *>(iD),
                       static_cast<VTK_TT *>(oD),numTuples,inArray->GetNumberOfComponents(),
                       outArray,static_cast<VTK_TT>(nullValue)));
    }//over all VTK types
  }//promote integral types

  assert(outArray->GetReferenceCount() > 1);
  outArray->FastDelete();
  return outArray;
}

//----------------------------------------------------------------------------
// Add the arrays to interpolate here. This presumes that vtkDataSetAttributes::CopyData() or
// vtkDataSetAttributes::InterpolateData() has been called, and the input and output array
// names match.
inline void ArrayList::
AddArrays(vtkIdType numOutPts, vtkDataSetAttributes *inPD, vtkDataSetAttributes *outPD,
          double nullValue, bool promote)
{
  // Build the vector of interpolation pairs. Note that InterpolateAllocate should have
  // been called at this point (output arrays created and allocated).
  char *name;
  vtkDataArray *iArray, *oArray;
  int iType, oType;
  void *iD, *oD;
  int iNumComp, oNumComp;
  int i, numArrays = outPD->GetNumberOfArrays();

  for (i=0; i < numArrays; ++i)
  {
    oArray = outPD->GetArray(i);
    if ( oArray && ! this->IsExcluded(oArray) )
    {
      name = oArray->GetName();
      iArray = inPD->GetArray(name);
      if ( iArray && ! this->IsExcluded(iArray) )
      {
        iType = iArray->GetDataType();
        oType = oArray->GetDataType();
        iNumComp = iArray->GetNumberOfComponents();
        oNumComp = oArray->GetNumberOfComponents();
        if ( promote && oType != VTK_FLOAT && oType != VTK_DOUBLE )
        {
          oType = VTK_FLOAT;
          vtkFloatArray *fArray = vtkFloatArray::New();
          fArray->SetName(oArray->GetName());
          fArray->SetNumberOfComponents(oNumComp);
          outPD->AddArray(fArray); //nasty side effect will replace current array in the same spot
          oArray = fArray;
          fArray->Delete();
        }
        oArray->SetNumberOfTuples(numOutPts);

        assert( iNumComp == oNumComp );
        if ( iType == oType )
        {
          iD = iArray->GetVoidPointer(0);
          oD = oArray->GetVoidPointer(0);
          switch (iType)
          {
            vtkTemplateMacro(CreateArrayPair(this, static_cast<VTK_TT *>(iD),
                             static_cast<VTK_TT *>(oD),numOutPts,oNumComp,
                             oArray,static_cast<VTK_TT>(nullValue)));
          }//over all VTK types
        }//if matching types
        else //promoted type
        {
          iD = iArray->GetVoidPointer(0);
          oD = oArray->GetVoidPointer(0);
          switch (iType)
          {
            vtkTemplateMacro(CreateRealArrayPair(this, static_cast<VTK_TT *>(iD),
                             static_cast<float*>(oD),numOutPts,iNumComp,
                             oArray,static_cast<float>(nullValue)));
          }//over all VTK types
        }//if promoted pair
      }//if matching input array
    }//if output array
  }//for each candidate array
}

//----------------------------------------------------------------------------
// Add the arrays to interpolate here. This presumes that vtkDataSetAttributes::CopyData() or
// vtkDataSetAttributes::InterpolateData() has been called. This special version creates an
// array pair that interpolates from itself.
inline void ArrayList::
AddSelfInterpolatingArrays(vtkIdType numOutPts, vtkDataSetAttributes *attr, double nullValue)
{
  // Build the vector of interpolation pairs. Note that CopyAllocate/InterpolateAllocate should have
  // been called at this point (output arrays created and allocated).
  vtkDataArray *iArray;
  int iType, iNumComp;
  void *iD;
  int i, numArrays = attr->GetNumberOfArrays();

  for (i=0; i < numArrays; ++i)
  {
    iArray = attr->GetArray(i);
    if ( iArray && ! this->IsExcluded(iArray) )
    {
      iType = iArray->GetDataType();
      iNumComp = iArray->GetNumberOfComponents();
      iArray->WriteVoidPointer(0,numOutPts*iNumComp); //allocates memory, preserves data
      iD = iArray->GetVoidPointer(0);
      switch (iType)
      {
        vtkTemplateMacro(CreateArrayPair(this, static_cast<VTK_TT *>(iD),
                         static_cast<VTK_TT *>(iD),numOutPts,iNumComp,
                         iArray,static_cast<VTK_TT>(nullValue)));
      }//over all VTK types
    }//if not excluded
  }//for each candidate array
}

#endif
