// This program shows that a vtkUnstructured grid passed through a
// vtkGeometryFilter does not correctly process the cell data

#include <iostream>

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"

#include "vtkType.h"
#include "vtkIdTypeArray.h"
#include "vtkUnstructuredGrid.h"
#include "vtkGeometryFilter.h"
#include "vtkSmartPointer.h"

void filter( vtkUnstructuredGrid* ug );
void PrintDataSet( vtkDataSet* d );
void PrintFieldData( vtkFieldData* fd );

// Creates a vtkUnstructuredGrid
class GridFactory
{
public:
  GridFactory();

  void addVertexCells();
  void addLineCells();
  void addTriangleCells();
  void addTetraCells();
  vtkUnstructuredGrid* get();

private:
  vtkUnstructuredGrid* m_grid;
};

GridFactory::GridFactory() :
  m_grid ( vtkUnstructuredGrid::New() )
{
  // the points
  static float x[8][3]={{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
                        {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}};

  std::cout << "Defining 8 points\n";
  // Create the points
  vtkPoints* points = vtkPoints::New();
  points->SetNumberOfPoints( 8 );
  for ( int i = 0; i < 8; ++i )
  {
    points->SetPoint( i, x[i] );
  }
  m_grid->SetPoints( points );
}

// Create 2 tetras
void GridFactory::addTetraCells()
{
  if ( ! m_grid )
    return;

  std::cout << "Adding 2 tetra cells\n";
  vtkIdType pts[4];
  pts[0] = 0; pts[1] = 1; pts[2] = 2; pts[3] = 3;
  m_grid->InsertNextCell( VTK_TETRA, 4, pts );
  pts[0] = 2; pts[1] = 3; pts[2] = 4; pts[3] = 5;
  m_grid->InsertNextCell( VTK_TETRA, 4, pts );
}

// Create 2 triangles
void GridFactory::addTriangleCells()
{
  if ( ! m_grid )
    return;

  std::cout << "Adding 2 triangle cells\n";

  vtkIdType pts[3];
  pts[0] = 1; pts[1] = 3; pts[2] = 5;
  m_grid->InsertNextCell( VTK_TRIANGLE, 3, pts );
  pts[0] = 2; pts[1] = 4; pts[2] = 6;
  m_grid->InsertNextCell( VTK_TRIANGLE, 3, pts );
}

// Create 2 lines
void GridFactory::addLineCells()
{
  if ( ! m_grid )
    return;

  std::cout << "Adding 2 line cells\n";

  vtkIdType pts[2];
  pts[0] = 3; pts[1] = 7;
  m_grid->InsertNextCell( VTK_LINE, 2, pts );
  pts[0] = 0; pts[1] = 4;
  m_grid->InsertNextCell( VTK_LINE, 2, pts );
}

// Create 2 points
void GridFactory::addVertexCells()
{
  if ( ! m_grid )
    return;

  std::cout << "Adding 2 vertex cells\n";

  vtkIdType pts[1];
  pts[0] = 7;
  m_grid->InsertNextCell( VTK_VERTEX, 1, pts );
  pts[0] = 6;
  m_grid->InsertNextCell( VTK_VERTEX, 1, pts );
}

// Add cell data and point data for all cells/points, and
// return the unstructured grid.
vtkUnstructuredGrid* GridFactory::get()
{
  if ( ! m_grid )
    return NULL;

  // Create a point data array
  const char* name = "foo";
  int num = m_grid->GetNumberOfPoints();
  std::cout << "Adding point data array '"<<name<<"' with data for "<<num<<" points\n";
  vtkIdTypeArray* pointDataArray = vtkIdTypeArray::New();
  pointDataArray->SetName( name );
  // Creating data for 8 points
  for ( int i = 0; i < num; ++i )
  {
    vtkIdType value = i+100;
    pointDataArray->InsertNextTupleValue( &value );
  }
  m_grid->GetPointData()->AddArray( pointDataArray );

  // Create the cell data array
  name = "bar";
  num = m_grid->GetNumberOfCells();
  std::cout << "Adding cell data array '"<<name<<"' with data for "<<num<<" cells\n";
  vtkIdTypeArray* cellDataArray = vtkIdTypeArray::New();
  cellDataArray->SetName( name );
  cellDataArray->SetNumberOfComponents( 1 );
  for ( int i = 0; i < num; ++i )
  {
    vtkIdType value = i+200;
    cellDataArray->InsertNextTupleValue( &value );
  }
  m_grid->GetCellData()->AddArray( cellDataArray );

  vtkUnstructuredGrid* g = m_grid;
  m_grid = NULL;
  return g;
}

int main()
{
  vtkSmartPointer<vtkUnstructuredGrid> ug;

  // Build the unstructured grid
  GridFactory g;
  g.addTetraCells();
  g.addTriangleCells();
  g.addLineCells();
  g.addVertexCells();
  ug = g.get();

  // Run it through vtkGeometryFilter
  filter( ug );

  return 0;
}

// Runs the unstructured grid through the vtkGeometryFilter and prints the
// output
void filter( vtkUnstructuredGrid* ug )
{
  // Print the input unstructured grid dataset
  std::cout << "\nvtkGeometryFilter input:\n";
  PrintDataSet( ug );

  // Do the filtering
  vtkSmartPointer<vtkGeometryFilter> gf = vtkGeometryFilter::New();
  gf->SetInput( ug );
  gf->Update();

  // Print the output poly data
  std::cout << "\nvtkGeometryFilter output:\n";
  PrintDataSet( gf->GetOutput( ) );
}

void PrintDataSet( vtkDataSet* d )
{
  if ( ! d )
  {
    std::cout << "No dataset\n";
    return;
  }

  const char* name;
  if ( vtkUnstructuredGrid::SafeDownCast( d ) )
    name = "vtkUnstructuredGrid";
  else if ( vtkPolyData::SafeDownCast( d ) )
    name = "vtkPolyData";
  else
    name = "vtkDataSet";

  std::cout << name
            << " dimensions: #cells="<<d->GetNumberOfCells()
            << " #points=" << d->GetNumberOfPoints()<< "\n";
  PrintFieldData( d->GetPointData() );
  PrintFieldData( d->GetCellData() );
}

void PrintFieldData( vtkFieldData* fd )
{
  if ( ! fd )
  {
    std::cout << "No field data\n";
    return;
  }

  const char* name;
  if ( vtkCellData::SafeDownCast( fd ) )
    name = "cell data";
  else if ( vtkPointData::SafeDownCast( fd ) )
    name = "point data";
  else
    name = "field data";

  for ( int i = 0; i < fd->GetNumberOfArrays(); ++i )
  {
    vtkAbstractArray *a = fd->GetArray( i );
    std::cout<< name << " array '" << a->GetName() << "' #tuples="<< a->GetNumberOfTuples()<<"\n";
  }
}

