#include "vtkCPInputDataDescription.h"
#include "vtkCTHSource.h"
#include "vtkCTHDataArray.h"
#include "vtkObjectFactory.h"
#include "vtkInformationVector.h"
#include "vtkInformation.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkNonOverlappingAMR.h"
#include "vtkAMRBox.h"
#include "vtkUniformGrid.h"
#include "vtkBoundingBox.h"
#include "vtkSmartPointer.h"
#include "vtkCellData.h"
#include "vtkPointData.h"
#include "vtkUnsignedCharArray.h"
#include "vtkIntArray.h"
#include "vtkMultiProcessController.h"
//---------------------------------------------------------------------------
vtkCTHSource::vtkCTHSource ()
{
}

vtkCTHSource::~vtkCTHSource ()
{
  if (NeighborArray) 
    {
    NeighborArray->Delete ();
    NeighborArray = 0;
    }
  for (size_t i = 0; i < this->Blocks.size (); i ++)
    {
    Block &b = this->Blocks[i];
    if (b.allocated)
      {
      b.ug->Delete ();
      delete b.box;
      }
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::Initialize (int vtkNotUsed (igm), int n_blocks, 
   int nmat, int vtkNotUsed(max_mat),
   int NCFieldNames, int NMFieldNames, 
   double *min, double *max, int max_level)
{
  this->MaxLevel = max_level;
  this->Bounds.SetMinPoint (min);
  this->Bounds.SetMaxPoint (max);
  this->CFieldNames.resize (NCFieldNames);
  this->MFieldNames.resize (nmat);

  for (int i = 0; i < nmat; i ++) 
    {
    this->MFieldNames[i].resize (NMFieldNames);
    }

  this->Blocks.clear ();
  this->Blocks.resize (n_blocks);

  // These are set during block initialization
  this->MinLevel = this->MaxLevel + 1; 
  this->GlobalBlockSize[0] = 0;
  this->GlobalBlockSize[1] = 0;
  this->GlobalBlockSize[2] = 0;

  this->NeighborArray = vtkIntArray::New ();
  this->NeighborArray->SetNumberOfComponents (1);
  this->NeighborArray->SetName ("Neighbors");
}

//---------------------------------------------------------------------------
void vtkCTHSource::SetCellFieldName (int field_id, char *field_name, 
   char *comment, int vtkNotUsed(matid))
{
  if (strncmp (field_name, "P ", 2) && strncmp (field_name, "T ", 2) &&
      strncmp (field_name, "VX ", 3) && strncmp (field_name, "VY ", 3) && 
      strncmp (field_name, "VZ ", 3))
    {
    CFieldNames[field_id] = std::string();
    } 
  else
    {
    // strip off the trailing "!" from the comment
    char *end = strchr (comment, '!');
    CFieldNames[field_id] = std::string(comment, end - comment);
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::SetMaterialFieldName (int field_id, char *field_name, 
    char *comment)
{
  size_t mats = this->MFieldNames.size ();
  if (strncmp (field_name, "VOLM ", 5) && strncmp (field_name, "M ", 2)) 
  // if (strncmp (field_name, "VOLM ", 5)) 
    {
    std::string s;
    for (size_t i = 0; i < mats; i ++)
      {
      MFieldNames[i][field_id] = s;
      }
    }
  else
    {
    for (size_t i = 0; i < mats; i ++)
      {
      // strip off the trailing "!" from the comment
      char *end = strchr (comment, '!');
      std::string s (comment, end - comment);
      char n[13];
      sprintf (n, " - %lu", i + 1);
      s.append (n);
      MFieldNames[i][field_id] = s;
      }
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::SetCellFieldPointer (int block_id, int field_id, 
    int k, int j, double *istrip)
{
  Block &b = this->Blocks[block_id];
  if (b.CFieldData[field_id] != 0) 
    {
    /// TODO fix for multiple components
    b.CFieldData[field_id]->SetDataPointer (0, k, j, istrip);
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::SetMaterialFieldPointer (int block_id, int field_id, 
    int mat, int k, int j, double *istrip)
{
  Block &b = this->Blocks[block_id];
  if (b.MFieldData[mat][field_id] != 0)
    {
    /// TODO fix for multiple components
    b.MFieldData[mat][field_id]->SetDataPointer (0, k, j, istrip);
    if (b.actualMaterials <= mat)
      {
      b.actualMaterials = mat + 1;
      }
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::InitializeBlock(int block_id, int Nx, int Ny, int Nz, 
    double* x, double* y, double* z, int allocated, int active, int level)
{
  Block& b = this->Blocks[block_id];
  b.id = block_id;
  b.allocated = allocated;
  b.active = active;
  b.level = level;
  b.ug = 0;
  b.Nx = Nx;
  b.Ny = Ny;
  b.Nz = Nz;
  b.x = x;
  b.y = y;
  b.z = z;
  // set the placeholders for the strips passed in by scx/smx
  size_t numCFields = this->CFieldNames.size ();
  b.CFieldData.resize (numCFields);
  for (size_t c = 0; c < numCFields; c ++)
    {
    if (!this->CFieldNames[c].empty ()) 
      {
      b.CFieldData[c] = vtkSmartPointer<vtkCTHDataArray>::New ();
      b.CFieldData[c]->SetName (this->CFieldNames[c].c_str ());
      b.CFieldData[c]->SetDimensions (b.Nx, b.Ny, b.Nz);
      }
    else
      {
      b.CFieldData[c] = 0;
      }
    }
  b.actualMaterials = 0; // so far we've filled none
  size_t numMFields = this->MFieldNames.size ();
  b.MFieldData.resize (numMFields);
  for (size_t m = 0; m < numMFields; m ++)
    {
    size_t numFields = this->MFieldNames[m].size ();
    b.MFieldData[m].resize (numFields);
    for (size_t f = 0; f < numFields; f ++)
      {
      if (!this->MFieldNames[m][f].empty ()) 
        {
        b.MFieldData[m][f] = vtkSmartPointer<vtkCTHDataArray>::New ();
        b.MFieldData[m][f]->SetName (this->MFieldNames[m][f].c_str ());
        b.MFieldData[m][f]->SetDimensions (b.Nx, b.Ny, b.Nz);
        }
      else
        {
        b.MFieldData[m][f] = 0;
        }
      }
    }

  this->AllocationsChanged = true;

  if (this->GlobalBlockSize[0] == 0 && 
      this->GlobalBlockSize[1] == 0 && 
      this->GlobalBlockSize[2] == 0)
    {
    this->GlobalBlockSize[0] = Nx;
    this->GlobalBlockSize[1] = Ny;
    this->GlobalBlockSize[2] = Nz;
    }
  else 
    {
    if (this->GlobalBlockSize[0] != -1 && this->GlobalBlockSize[0] != Nx)
      {
      this->GlobalBlockSize[0] = -1;
      }
    if (this->GlobalBlockSize[1] != -1 && this->GlobalBlockSize[1] != Ny)
      {
      this->GlobalBlockSize[1] = -1;
      }
    if (this->GlobalBlockSize[2] != -1 && this->GlobalBlockSize[2] != Nz)
      {
      this->GlobalBlockSize[2] = -1;
      }
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::UpdateBlock(int block_id, int allocated, int active, 
    int level, int max_level, int bxbot, int bxtop, int bybot, int bytop, 
    int bzbot, int bztop, int* neighb_proc, int* neighb_block)
{
  Block &b = this->Blocks[block_id];
  b.active = active;
  b.bot[0] = bxbot;
  b.bot[1] = bybot;
  b.bot[2] = bzbot;
  b.top[0] = bxtop;
  b.top[1] = bytop;
  b.top[2] = bztop;

  memcpy (b.neighbor_proc, neighb_proc, 24 * sizeof (int));
  memcpy (b.neighbor_block, neighb_block, 24 * sizeof (int));

  if (b.allocated != allocated)
    {
    b.allocated = allocated;
    this->AllocationsChanged = true;
    }
  b.level = level;
  this->MaxLevel = max_level;

  if (b.allocated && b.level < this->MinLevel)
    {
    this->MinLevel = b.level;
    this->MinLevelSpacing[0] = b.x[1] - b.x[0];
    this->MinLevelSpacing[1] = b.y[1] - b.y[0];
    this->MinLevelSpacing[2] = b.z[1] - b.z[0];
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::UpdateRepresentation ()
{
  // A call to viz implies that our data is modified.
  for (size_t i = 0; i < Blocks.size (); i ++)
    {
    Block &b = this->Blocks[i]; 
    if (!b.allocated) 
      {
      continue;
      }
    for (size_t c = 0; c < b.CFieldData.size (); c ++)
      {
      if (b.CFieldData[c] == 0) continue;
      b.CFieldData[c]->Modified ();
/*
      vtkDataArray* da = b.ug->GetCellData ()->GetArray (b.CFieldData[c]->GetName ());
      if (da) 
        {
        int len = b.CFieldData[c]->GetNumberOfTuples ();
        for (int i = 0; i < len; i ++)
          {
          double *tup = b.CFieldData[c]->GetTuple (i);
          da->SetComponent (i, 0, tup[0]);
          }
        }
*/
      }
    for (size_t m = 0; m < b.MFieldData.size (); m ++)
      {
      for (size_t f = 0; f < b.MFieldData[m].size (); f ++)
        {
        if (b.MFieldData[m][f] == 0) continue;
        if (strncmp (b.MFieldData[m][f]->GetName (), "Volume Fraction", 15) != 0) 
          {
          b.MFieldData[m][f]->Modified ();
/*
          vtkDataArray* da = b.ug->GetCellData ()->GetArray (b.MFieldData[m][f]->GetName ());
          if (da) 
            {
            int len = b.MFieldData[m][f]->GetNumberOfTuples ();
            for (int i = 0; i < len; i ++)
              {
              double *tup = b.MFieldData[m][f]->GetTuple (i);
              da->SetComponent (i, 0, tup[0]);
              }
            }
*/
          }
        else 
          {
          vtkDataArray* da = b.ug->GetCellData ()->GetArray (b.MFieldData[m][f]->GetName ());
          if (da) 
            {
            int len = b.MFieldData[m][f]->GetNumberOfTuples ();
            for (int idx = 0; idx < len; idx ++)
              {
              double *tup = b.MFieldData[m][f]->GetTuple (idx);
              da->SetComponent (idx, 0, tup[0] * 255.0);
              }
            }
          }
        }
      }
    }
  this->AMRSet->Modified ();
}

//---------------------------------------------------------------------------
int vtkCTHSource::FillInputData (vtkCPInputDataDescription *input)
{
  if (this->AllocationsChanged)
    {
    this->AMRSet = vtkSmartPointer<vtkNonOverlappingAMR>::New ();
    input->SetGrid (this->AMRSet);

    std::vector<int> datasets (this->MaxLevel + 1, 0);
    // clear out the hierarchy
    for (size_t i = 0; i < Blocks.size (); i ++)
      {
      Block &b = this->Blocks[i]; 
      datasets[b.level] ++;
      }
    AMRSet->Initialize (this->MaxLevel + 1, &datasets[0]); 

    this->NeighborArray->SetNumberOfTuples (0);
    for (size_t i = 0; i < Blocks.size (); i ++)
      {
      Block &b = this->Blocks[i]; 
      if (b.ug)
        {
        b.ug->Delete ();
        b.ug = 0;
        delete b.box;
        }
      if (b.allocated) 
        {
        AllocateBlock (b);
        datasets[b.level] --;
        AMRSet->SetDataSet (b.level, datasets[b.level], b.ug);
        }
      }

    this->AddAttributesToAMR (AMRSet);
    // this->AllocationsChanged = false;
    } 
  UpdateRepresentation ();

  return 1;
}

//---------------------------------------------------------------------------
void vtkCTHSource::AllocateBlock (Block& b)
{
  int loCorner[3];
  int hiCorner[3];

  bool extentsChanged = this->GetBounds (b, loCorner, hiCorner);

  b.box = new vtkAMRBox (loCorner, hiCorner);

  int dx = hiCorner[0] - loCorner[0] + 1;
  int dy = hiCorner[1] - loCorner[1] + 1;
  int dz = hiCorner[2] - loCorner[2] + 1;

  b.ug = vtkUniformGrid::New ();
  b.ug->SetExtent (0, dx, 0, dy, 0, dz);
  b.ug->SetSpacing (b.x[1] - b.x[0], b.y[1] - b.y[0], b.z[1] - b.z[0]);
  b.ug->SetOrigin (b.x[loCorner[0]], b.y[loCorner[1]], b.z[loCorner[2]]);
  
  this->AddGhostArray (b, dx, dy, dz);
  this->AddBlockIdArray (b, dx, dy, dz);
  this->AddNeighborArray (b);
  
  // Need to add this since if we do Cutter first ,
  // it no longer looks hierarchical to PVGeomFilter
  // this->AddAMRLevelArray (b, dx, dy, dz);
  
  if (extentsChanged)
    {
    this->AddFieldArrays (b, loCorner, hiCorner);
    // this->AddFieldArrays (b);
    }
  else
    {
    this->AddFieldArrays (b);
    }

  // this->AddActivationArray (b);
}

//---------------------------------------------------------------------------
bool vtkCTHSource::GetBounds (Block &b, int loCorner[3], int hiCorner[3])
{
  loCorner[0] = 0;
  loCorner[1] = 0;
  loCorner[2] = 0;
  hiCorner[0] = b.Nx - 1; 
  hiCorner[1] = b.Ny - 1; 
  hiCorner[2] = b.Nz - 1;
  const double *min = this->Bounds.GetMinPoint ();
  const double *max = this->Bounds.GetMaxPoint ();
  bool extentsChanged = false;
  // CTH allows the blocks to extent one cell outside the real bounds
  // Normally for blocks these extra cells are ghost cells, 
  // but outside the simulation bounds, we don't want to viz them at all
  if (b.x[0] < min[0])
    {
    loCorner[0] ++;
    extentsChanged = true;
    }
  if (b.x[b.Nx-1] > max[0])
    {
    hiCorner[0] --;
    extentsChanged = true;
    }
  if (b.y[0] < min[1])
    {
    loCorner[1] ++;
    extentsChanged = true;
    }
  if (b.y[b.Ny-1] > max[1])
    {
    hiCorner[1] --;
    extentsChanged = true;
    }
  if (b.z[0] < min[2])
    {
    loCorner[2] ++;
    extentsChanged = true;
    }
  if (b.z[b.Nz-1] > max[2])
    {
    hiCorner[2] --;
    extentsChanged = true;
    }
  // if they're changed the CTHDataArray needs to know about it
  return extentsChanged;
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddGhostArray (Block& b, int dx, int dy, int dz)
{
  vtkCellData* cd = b.ug->GetCellData ();

  // The outside edge cells (not global bounds) are ghost cells level 1
  vtkUnsignedCharArray *ghostArray = vtkUnsignedCharArray::New ();
  ghostArray->SetNumberOfTuples (dx*dy*dz);
  ghostArray->SetName (vtkDataSetAttributes::GhostArrayName());
  cd->AddArray (ghostArray);
  ghostArray->Delete ();

  int ghostsLower[3];
  int ghostsUpper[3];
  for (int i = 0; i < 3; i ++)
    {
    ghostsLower[i] = (b.active && b.bot[i] < 0);
    ghostsUpper[i] = (b.active && b.top[i] < 0);
    }

  unsigned char *ptr = static_cast<unsigned char*>(ghostArray->GetVoidPointer(0));
  for (int k = 0; k < dz; k ++)
    {
    if ((dz > 1) && ((ghostsLower[2] && (k == 0)) ||
                      (ghostsUpper[2] && (k == (dz - 1)))))
      {
      memset (ptr, vtkDataSetAttributes::DUPLICATECELL, dx * dy);
      ptr += dx * dy;
      continue;
      }
    for (int j = 0; j < dy; j ++)
      {
      if ((dy > 1) && ((ghostsLower[1] && (j == 0)) ||
                        (ghostsUpper[1] && (j == (dy - 1)))))
        {
        memset (ptr, vtkDataSetAttributes::DUPLICATECELL, dx);
        }
      else
        {
        memset (ptr, 0, dx);
        if (dx > 1)
          {
          if (ghostsLower[0])
            {
            ptr[0] = 1;
            }
          if (ghostsUpper[0])
            {
            ptr[dx - 1] = 1;
            }
          }
        }
      ptr += dx;
      }
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddBlockIdArray (Block& b, int dx, int dy, int dz)
{
  vtkCellData* cd = b.ug->GetCellData ();

  vtkIntArray *blockArray = vtkIntArray::New ();
  blockArray->SetNumberOfTuples (dx*dy*dz);
  blockArray->SetName ("ElementBlockIds");
  cd->AddArray (blockArray);
  blockArray->Delete ();

  int *ptr = static_cast<int*>(blockArray->GetVoidPointer(0));
  memset (ptr, b.id, dx * dy * dz);
}

void vtkCTHSource::AddNeighborArray (Block& b) 
{
  vtkMultiProcessController* ctrl = vtkMultiProcessController::GetGlobalController ();
  int localId = ctrl->GetLocalProcessId ();

  int len = 24; // known quantity passed in from Adaptor
  for (int i = 0; i < len; i ++) 
    {
    if (b.neighbor_block[i] == 0 || b.neighbor_proc[i] == localId) 
      {
      continue;
      }
    bool present = false;
    for (int j = 0; j < NeighborArray->GetNumberOfTuples (); j ++) 
      {
      int compare;
      NeighborArray->GetTupleValue (j, &compare);
      if (b.neighbor_proc[i] == compare)
        {
        present = true;
        break;
        }
      }
    if (!present) 
      {
      NeighborArray->InsertNextTupleValue (&(b.neighbor_proc[i]));
      }

    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddAMRLevelArray (Block& b, int dx, int dy, int dz)
{
  vtkUnsignedCharArray *amrLevel = vtkUnsignedCharArray::New ();
  amrLevel->SetNumberOfTuples (dx*dy*dz);
  amrLevel->SetName ("vtkAMRLevel");
  memset (amrLevel->GetVoidPointer (0), b.level, 
          sizeof (unsigned char) * dx * dy * dz);
  b.ug->GetCellData ()->AddArray (amrLevel);
  amrLevel->Delete ();
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddFieldArrays (Block& b)
{
  vtkCellData* cd = b.ug->GetCellData ();

  for (size_t c = 0; c < b.CFieldData.size (); c ++)
    {
    if (b.CFieldData[c] == 0) continue;
    b.CFieldData[c]->UnsetExtents ();
    cd->AddArray (b.CFieldData[c]);
/*
    vtkDoubleArray* copied = vtkDoubleArray::New ();
    copied->SetName (b.CFieldData[c]->GetName ());
    copied->SetNumberOfComponents (1);
    copied->SetNumberOfTuples (b.CFieldData[c]->GetNumberOfTuples ());
    cd->AddArray (copied);
    copied->Delete ();
*/
    }
  for (int m = 0; m < b.actualMaterials; m ++)
    {
    // TODO In general for in situ, we should only pull the data we'd actually want.
    for (size_t f = 0; f < b.MFieldData[m].size (); f ++)
      {
      if (b.MFieldData[m][f] == 0) continue;
      b.MFieldData[m][f]->UnsetExtents ();
      if (strncmp (b.MFieldData[m][f]->GetName (), "Volume Fraction", 15) != 0) 
        {
        cd->AddArray (b.MFieldData[m][f]);
/*
        vtkDoubleArray* copied = vtkDoubleArray::New ();
        copied->SetName (b.MFieldData[m][f]->GetName ());
        copied->SetNumberOfComponents (1);
        copied->SetNumberOfTuples (b.MFieldData[m][f]->GetNumberOfTuples ());
        cd->AddArray (copied);
        copied->Delete ();
*/
        }
      else 
        {
        vtkIntArray* rounded = vtkIntArray::New ();
        rounded->SetName (b.MFieldData[m][f]->GetName ());
        rounded->SetNumberOfComponents (1);
        rounded->SetNumberOfTuples (b.MFieldData[m][f]->GetNumberOfTuples ());
        cd->AddArray (rounded);
        rounded->Delete ();
        }
      } 
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddFieldArrays (Block& b, 
                                   int loCorner[3], int hiCorner[3])
{
  vtkCellData* cd = b.ug->GetCellData ();

  for (size_t c = 0; c < b.CFieldData.size (); c ++)
    {
    if (b.CFieldData[c] == 0) continue;
    b.CFieldData[c]->SetExtents (loCorner, hiCorner);
    cd->AddArray (b.CFieldData[c]);
/*
    vtkDoubleArray* copied = vtkDoubleArray::New ();
    copied->SetName (b.CFieldData[c]->GetName ());
    copied->SetNumberOfComponents (1);
    copied->SetNumberOfTuples (b.CFieldData[c]->GetNumberOfTuples ());
    cd->AddArray (copied);
    copied->Delete ();
*/
    }
  for (int m = 0; m < b.actualMaterials; m ++)
    {
    // TODO In general for in situ, we should only pull the data we'd actually want.
    for (size_t f = 0; f < b.MFieldData[m].size (); f ++)
      {
      if (b.MFieldData[m][f] == 0) continue;
      b.MFieldData[m][f]->SetExtents (loCorner, hiCorner);
      if (strncmp (b.MFieldData[m][f]->GetName (), "Volume Fraction", 15) != 0) 
        {
        cd->AddArray (b.MFieldData[m][f]);
/*
        vtkDoubleArray* copied = vtkDoubleArray::New ();
        copied->SetName (b.MFieldData[m][f]->GetName ());
        copied->SetNumberOfComponents (1);
        copied->SetNumberOfTuples (b.MFieldData[m][f]->GetNumberOfTuples ());
        cd->AddArray (copied);
        copied->Delete ();
*/
        }
      else 
        {
        vtkIntArray* rounded = vtkIntArray::New ();
        rounded->SetName (b.MFieldData[m][f]->GetName ());
        rounded->SetNumberOfComponents (1);
        rounded->SetNumberOfTuples (b.MFieldData[m][f]->GetNumberOfTuples ());
        cd->AddArray (rounded);
        rounded->Delete ();
        }
      } 
    }
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddActivationArray (Block& b)
{
  vtkCellData* cd = b.ug->GetCellData ();
  vtkDataArray* da = cd->GetArray ("ActiveBlock");
  if (!da)
    {
    da = vtkUnsignedCharArray::New ();
    da->SetName ("ActiveBlock");
    da->SetNumberOfTuples (b.ug->GetNumberOfCells ());
    cd->AddArray (da);
    da->Delete ();
    }
  da->FillComponent (0, b.active);
}

//---------------------------------------------------------------------------
void vtkCTHSource::AddAttributesToAMR (vtkNonOverlappingAMR* amr)
{
  vtkFieldData *fd = amr->GetFieldData ();

  double b[6];
  this->Bounds.GetBounds (b);
  vtkDoubleArray *bounds = vtkDoubleArray::New ();
  bounds->SetNumberOfTuples (6);
  bounds->SetName ("GlobalBounds");
  for (int i = 0; i < 6; i ++)
    {
    bounds->SetValue (i, b[i]);
    }
  fd->AddArray (bounds);
  bounds->Delete ();

  vtkIntArray *boxSize = vtkIntArray::New ();
  boxSize->SetNumberOfTuples (3);
  boxSize->SetName ("GlobalBoxSize");
  for (int j = 0; j < 3; j ++)
    {
    boxSize->SetValue (j, this->GlobalBlockSize[j]);
    } 
  fd->AddArray (boxSize);
  boxSize->Delete ();

  vtkIntArray *minLevel = vtkIntArray::New ();
  minLevel->SetNumberOfTuples (1);
  minLevel->SetName ("MinLevel");
  minLevel->SetValue (0, this->MinLevel);
  fd->AddArray (minLevel);
  minLevel->Delete ();

  vtkDoubleArray *minLevelSpacing = vtkDoubleArray::New ();
  minLevelSpacing->SetNumberOfTuples (3);
  minLevelSpacing->SetName ("MinLevelSpacing");
  for (int k = 0; k < 3; k ++)
    {
    minLevelSpacing->SetValue (k, this->MinLevelSpacing[k]);
    }
  fd->AddArray (minLevelSpacing);
  minLevelSpacing->Delete ();

  if (this->NeighborArray->GetNumberOfTuples () > 0) 
    {
    fd->AddArray (this->NeighborArray);
    }
}
