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

  Program:   ParaView
  Module:    vtkAttributeDataToTableFilter.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkAttributeDataToTableFilter.h"

#include "vtkCellData.h"
#include "vtkCharArray.h"
#include "vtkCompositeDataPipeline.h"
#include "vtkDataSet.h"
#include "vtkGraph.h"
#include "vtkIdTypeArray.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPointSet.h"
#include "vtkRectilinearGrid.h"
#include "vtkStructuredGrid.h"
#include "vtkTable.h"

vtkStandardNewMacro(vtkAttributeDataToTableFilter);
//----------------------------------------------------------------------------
vtkAttributeDataToTableFilter::vtkAttributeDataToTableFilter()
{
  this->FieldAssociation = vtkDataObject::FIELD_ASSOCIATION_POINTS;
  this->AddMetaData = false;
  this->GenerateOriginalIds = false;
}

//----------------------------------------------------------------------------
vtkAttributeDataToTableFilter::~vtkAttributeDataToTableFilter()
{
}

//----------------------------------------------------------------------------
vtkExecutive* vtkAttributeDataToTableFilter::CreateDefaultExecutive()
{
  return vtkCompositeDataPipeline::New();
}

//----------------------------------------------------------------------------
int vtkAttributeDataToTableFilter::FillInputPortInformation(
  int vtkNotUsed(port), vtkInformation* info)
{
  info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkTable");
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGraph");
  return 1;
}

//----------------------------------------------------------------------------
int vtkAttributeDataToTableFilter::RequestData(
  vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector,
  vtkInformationVector* outputVector)
{
  vtkDataObject* input = vtkDataObject::GetData(inputVector[0], 0);
  vtkFieldData* fieldData = this->GetSelectedField(input);

  if (fieldData)
    {
    vtkTable* output = vtkTable::GetData(outputVector);
    if (this->FieldAssociation ==
      vtkDataObject::FIELD_ASSOCIATION_NONE)
      {
      // Field data can have different length arrays, so we need to create
      // output vtkTable big enough to fit the largest array.
      this->PassFieldData(output->GetRowData(), fieldData);
      }
    else
      {
      output->GetRowData()->ShallowCopy(fieldData);
      if (this->FieldAssociation ==
        vtkDataObject::FIELD_ASSOCIATION_CELLS)
        {
        // BUG #6830. Add cell-type array.
        vtkDataSet* ds = vtkDataSet::SafeDownCast(input);
        if (ds)
          {
          vtkCharArray* celltypes = vtkCharArray::New();
          celltypes->SetName("Cell Type");
          vtkIdType numcells = ds->GetNumberOfCells();
          celltypes->SetNumberOfTuples(numcells);
          char* ptr = celltypes->GetPointer(0);
          for (vtkIdType cc=0; cc < numcells; cc++)
            {
            ptr[cc] = static_cast<char>(ds->GetCellType(cc));
            }
          output->GetRowData()->AddArray(celltypes);
          celltypes->Delete();
          }
        }
      }

    // Clear any attribute markings from the output. This resolves the problem
    // that GlobalNodeIds were not showing up in spreadsheet view.
    for (int cc=vtkDataSetAttributes::SCALARS;
      cc < vtkDataSetAttributes::NUM_ATTRIBUTES;
      cc++)
      {
      output->GetRowData()->SetActiveAttribute(-1, cc);
      }

    if (this->AddMetaData &&
      this->FieldAssociation !=
      vtkDataObject::FIELD_ASSOCIATION_NONE)
      {
      this->Decorate(output, input);
      }
    }
  return 1;
}

//----------------------------------------------------------------------------
void vtkAttributeDataToTableFilter::PassFieldData(vtkFieldData* output,
  vtkFieldData* input)
{
  output->DeepCopy(input);
  // Now resize arrays to match the longest one.
  vtkIdType max_Tuples = 0;
  int cc;
  for (cc=0; cc < output->GetNumberOfArrays(); cc++)
    {
    vtkAbstractArray* arr = output->GetAbstractArray(cc);
    if (arr && arr->GetNumberOfTuples() > max_Tuples)
      {
      max_Tuples = arr->GetNumberOfTuples();
      }
    }
  for (cc=0; cc < output->GetNumberOfArrays(); cc++)
    {
    vtkAbstractArray* arr = output->GetAbstractArray(cc);
    vtkIdType numTuples = arr->GetNumberOfTuples();
    if (numTuples != max_Tuples)
      {
      arr->Resize(max_Tuples);
      arr->SetNumberOfTuples(max_Tuples);
      int num_comps = arr->GetNumberOfComponents();

      vtkDataArray* da = vtkDataArray::SafeDownCast(arr);
      if (da)
        {
        double *tuple = new double[num_comps+1];
        for (int kk=0; kk < num_comps+1; kk++)
          {
          tuple[kk] = 0;
          }
        for (vtkIdType jj=numTuples; jj < max_Tuples; jj++)
          {
          da->SetTuple(jj, tuple);
          }
        }
      }
    }
}

//----------------------------------------------------------------------------
#define vtkAttributeDataToTableFilterValidate(type, method)\
{\
  type* __temp = type::SafeDownCast(input);\
  if (__temp)\
    {\
    return __temp->method();\
    }\
  return 0;\
}

//----------------------------------------------------------------------------
vtkFieldData* vtkAttributeDataToTableFilter::GetSelectedField(vtkDataObject* input)
{
  if (input)
    {
    switch (this->FieldAssociation)
      {
    case vtkDataObject::FIELD_ASSOCIATION_POINTS:
      vtkAttributeDataToTableFilterValidate(vtkDataSet, GetPointData);

    case vtkDataObject::FIELD_ASSOCIATION_CELLS:
      vtkAttributeDataToTableFilterValidate(vtkDataSet, GetCellData);

    case vtkDataObject::FIELD_ASSOCIATION_NONE:
      return input->GetFieldData();

    case vtkDataObject::FIELD_ASSOCIATION_VERTICES:
      vtkAttributeDataToTableFilterValidate(vtkGraph, GetVertexData);

    case vtkDataObject::FIELD_ASSOCIATION_EDGES:
      vtkAttributeDataToTableFilterValidate(vtkGraph, GetEdgeData);

    case vtkDataObject::FIELD_ASSOCIATION_ROWS:
      vtkAttributeDataToTableFilterValidate(vtkTable, GetRowData);
      }
    }
  return 0;
}

#define VTK_MAX(x, y) ((x) > (y))? (x) : (y)
//----------------------------------------------------------------------------
void vtkAttributeDataToTableFilter::Decorate(vtkTable* output,
  vtkDataObject* input)
{
  vtkPointSet* psInput = vtkPointSet::SafeDownCast(input);
  vtkRectilinearGrid* rgInput = vtkRectilinearGrid::SafeDownCast(input);
  vtkImageData* idInput = vtkImageData::SafeDownCast(input);
  vtkStructuredGrid* sgInput = vtkStructuredGrid::SafeDownCast(input);
  const int* dimensions = 0;
  if (rgInput)
    {
    dimensions = rgInput->GetDimensions();
    }
  else if (idInput)
    {
    dimensions = idInput->GetDimensions();
    }
  else if (sgInput)
    {
    dimensions = sgInput->GetDimensions();
    }

  int cellDims[3];
  if (this->FieldAssociation == vtkDataObject::FIELD_ASSOCIATION_CELLS &&
    dimensions)
    {
    cellDims[0] = VTK_MAX(1, (dimensions[0] -1));
    cellDims[1] = VTK_MAX(1, (dimensions[1] -1));
    cellDims[2] = VTK_MAX(1, (dimensions[2] -1));
    dimensions = cellDims;
    }

  if (this->FieldAssociation == vtkDataObject::FIELD_ASSOCIATION_POINTS &&
    psInput && psInput->GetPoints())
    {
    output->GetRowData()->AddArray(psInput->GetPoints()->GetData());
    }

  if (dimensions)
    {
    // I cannot decide if this should be put in the vtkInformation associated
    // with the vtkTable or in FieldData. I'd rather the former but not sure
    // how that's going to be propagated through the pipeline.
    vtkIntArray* dArray = vtkIntArray::New();
    dArray->SetName("STRUCTURED_DIMENSIONS");
    dArray->SetNumberOfComponents(3);
    dArray->SetNumberOfTuples(1);
    dArray->SetTupleValue(0, dimensions);
    output->GetFieldData()->AddArray(dArray);
    dArray->Delete();
    }

  if (this->GenerateOriginalIds)
    {
    // Add an original ids array. I know, this is going to bloat up the memory a
    // bit, but it's much easier to add the array now than later (esp. in
    // vtkSortedTableStreamer) since it's ending up buggy and hard to track.

    vtkIdTypeArray* indicesArray = vtkIdTypeArray::New();
    indicesArray->SetName("vtkOriginalIndices");
    indicesArray->SetNumberOfComponents(1);
    vtkIdType numElements = input->GetNumberOfElements(this->FieldAssociation);
    indicesArray->SetNumberOfTuples(numElements);
    for (vtkIdType cc=0; cc < numElements; cc++)
      {
      indicesArray->SetValue(cc, cc);
      }
    output->GetRowData()->AddArray(indicesArray);
    indicesArray->FastDelete();
    }
}

//----------------------------------------------------------------------------
void vtkAttributeDataToTableFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "FieldAssociation: " << this->FieldAssociation << endl;
  os << indent << "AddMetaData: " << this->AddMetaData << endl;
  os << indent << "GenerateOriginalIds: " << this->GenerateOriginalIds << endl;
}


