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

  Program:   Visualization Toolkit
  Module:    TestGeometryFilterCellData.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.

=========================================================================*/
// Test that the proper amount of tuples exist in the
// point and cell data arrays after the vtkGeometryFilter
// is used.

#include <iostream>

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkGeometryFilter.h"
#include "vtkIdTypeArray.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkSmartPointer.h"
#include "vtkUnstructuredGrid.h"

int TestGeometryFilter( vtkUnstructuredGrid* ug );
int CheckDataSet( vtkDataSet* d );
int CheckFieldData( vtkIdType numGridEntities, vtkFieldData* fd );

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

  void AddVertexCells();
  void AddLineCells();
  void AddTriangleCells();
  void AddTetraCells();
  vtkUnstructuredGrid* Get();

private:
  vtkSmartPointer<vtkUnstructuredGrid> Grid;
};

GridFactory::GridFactory() :
  Grid ( vtkSmartPointer<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
  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
  points->SetNumberOfPoints( 8 );
  for ( int i = 0; i < 8; ++i )
  {
    points->SetPoint( i, x[i] );
  }
  this->Grid->SetPoints( points );
}

// Create 2 tetras
void GridFactory::AddTetraCells()
{
  if ( ! this->Grid )
    return;

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

// Create 2 triangles
void GridFactory::AddTriangleCells()
{
  if ( ! this->Grid )
    return;

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

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

// Create 2 lines
void GridFactory::AddLineCells()
{
  if ( ! this->Grid )
    return;

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

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

// Create 2 points
void GridFactory::AddVertexCells()
{
  if ( ! this->Grid )
    return;

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

  vtkIdType pts[1];
  pts[0] = 7;
  this->Grid->InsertNextCell( VTK_VERTEX, 1, pts );
  pts[0] = 6;
  this->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 ( ! this->Grid )
    return nullptr;

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

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

  return this->Grid;
}

int TestGeometryFilterCellData(int, char*[])
{
  vtkSmartPointer<vtkUnstructuredGrid> ug;

  // Build the unstructured grid
  GridFactory g;
  g.AddTetraCells();
  g.AddTriangleCells();
  g.AddLineCells();
  g.AddVertexCells();
  ug = g.Get();

  // Run it through vtkGeometryFilter
  int retVal = TestGeometryFilter( ug );

  return retVal;
}

// Runs the unstructured grid through the vtkGeometryFilter and prints the
// output
int TestGeometryFilter( vtkUnstructuredGrid* ug )
{
  // Print the input unstructured grid dataset
  std::cout << "\nvtkGeometryFilter input:\n";
  int retVal = CheckDataSet( ug );

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

  // Print the output poly data
  std::cout << "\nvtkGeometryFilter output:\n";
  vtkPolyData *poly = vtkPolyData::SafeDownCast( gf->GetOutput( ) );
  retVal += CheckDataSet( poly );
  return retVal;
}

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

  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";
  int retVal = CheckFieldData( d->GetNumberOfPoints(), d->GetPointData() );
  retVal += CheckFieldData( d->GetNumberOfCells(), d->GetCellData() );
  return retVal;
}

int CheckFieldData( vtkIdType numGridEntities, vtkFieldData* fd )
{
  int retVal = 0;
  if ( ! fd )
  {
    std::cout << "No field data\n";
    return 1;
  }

  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 );
    if(a->GetNumberOfTuples() != numGridEntities)
    {
      vtkGenericWarningMacro(<< name << " array '" << a->GetName() << "' has #tuples="<< a->GetNumberOfTuples()
                             << " but should have " << numGridEntities);
      retVal = 1;
    }
  }
  return retVal;
}
