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

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

=========================================================================*/
// .NAME vtkBridgeDataSet - Implementation of vtkGenericDataSet.
// .SECTION Description
// It is just an example that show how to implement the Generic. It is also
// used for testing and evaluating the Generic.

#include "vtkBridgeDataSet.h"

#include <assert.h>

#include "vtkObjectFactory.h"
#include "vtkDataSet.h"
#include "vtkCellTypes.h"
#include "vtkCell.h"
#include "vtkBridgeCellIterator.h"
#include "vtkBridgePointIterator.h"
#include "vtkBridgeCell.h"
#include "vtkGenericCell.h"
#include "vtkMath.h"
#include "vtkGenericAttributeCollection.h"
#include "vtkPointData.h"
#include "vtkCellData.h"
#include "vtkBridgeAttribute.h"
#include "vtkGenericCellTessellator.h"
#include "vtkGenericEdgeTable.h"
#include "vtkSimpleCellTessellator.h"

vtkStandardNewMacro(vtkBridgeDataSet);

//----------------------------------------------------------------------------
// Default constructor.
vtkBridgeDataSet::vtkBridgeDataSet(  )
{
  this->Implementation = 0;
  this->Types = vtkCellTypes::New();
  this->Tessellator = vtkSimpleCellTessellator::New();
}

//----------------------------------------------------------------------------
vtkBridgeDataSet::~vtkBridgeDataSet(  )
{
  if(this->Implementation)
    {
    this->Implementation->Delete();
    }
  this->Types->Delete();
  // this->Tessellator is deleted in the superclass
}

//----------------------------------------------------------------------------
void vtkBridgeDataSet::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "implementation: ";
  if(this->Implementation==0)
    {
    os << "(none)" << endl;
    }
  else
    {
    this->Implementation->PrintSelf(os << endl, indent.GetNextIndent());
    }
}

//----------------------------------------------------------------------------
// Description:
// Return the dataset that will be manipulated through the adaptor interface.
vtkDataSet *vtkBridgeDataSet::GetDataSet()
{
  return this->Implementation;
}

//----------------------------------------------------------------------------
// Description:
// Set the dataset that will be manipulated through the adaptor interface.
// \pre ds_exists: ds!=0
void vtkBridgeDataSet::SetDataSet(vtkDataSet *ds)
{
  int i;
  int c;
  vtkPointData *pd;
  vtkCellData *cd;
  vtkBridgeAttribute *a;

  vtkSetObjectBodyMacro(Implementation,vtkDataSet,ds);
  // refresh the attribute collection
  this->Attributes->Reset();
  if(ds!=0)
    {
    // point data
    pd=ds->GetPointData();
    c=pd->GetNumberOfArrays();
    i=0;
    while(i<c)
      {
      a=vtkBridgeAttribute::New();
      a->InitWithPointData(pd,i);
      this->Attributes->InsertNextAttribute(a);
      a->Delete();
      ++i;
      }
    // same thing for cell data.
    cd=ds->GetCellData();
    c=cd->GetNumberOfArrays();
    i=0;
    while(i<c)
      {
      a=vtkBridgeAttribute::New();
      a->InitWithCellData(cd,i);
      this->Attributes->InsertNextAttribute(a);
      a->Delete();
      ++i;
      }
    this->Tessellator->Initialize(this);
    }
  this->Modified();
}

//----------------------------------------------------------------------------
// Description:
// Number of points composing the dataset. See NewPointIterator for more
// details.
// \post positive_result: result>=0
vtkIdType vtkBridgeDataSet::GetNumberOfPoints()
{
  vtkIdType result = 0;
  if(this->Implementation)
    {
    result = this->Implementation->GetNumberOfPoints();
    }
  assert("post: positive_result" && result>=0);
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Compute the number of cells for each dimension and the list of types of
// cells.
// \pre implementation_exists: this->Implementation!=0
void vtkBridgeDataSet::ComputeNumberOfCellsAndTypes()
{
  unsigned char type;
  vtkIdType cellId;
  vtkIdType numCells;
  vtkCell *c;

  if ( this->GetMTime() > this->ComputeNumberOfCellsTime ) // cache is obsolete
     {
     numCells = this->GetNumberOfCells();
     this->NumberOf0DCells=0;
     this->NumberOf1DCells=0;
     this->NumberOf2DCells=0;
     this->NumberOf3DCells=0;

     this->Types->Reset();

     if(this->Implementation!=0)
       {
       cellId=0;
       while(cellId<numCells)
         {
         c=this->Implementation->GetCell(cellId);
         switch(c->GetCellDimension())
           {
           case 0:
             this->NumberOf0DCells++;
             break;
           case 1:
             this->NumberOf1DCells++;
             break;
           case 2:
             this->NumberOf2DCells++;
             break;
           case 3:
             this->NumberOf3DCells++;
             break;
           }
         type=c->GetCellType();
         if(!this->Types->IsType(type))
           {
           this->Types->InsertNextType(type);
           }
         cellId++;
         }
       }

     this->ComputeNumberOfCellsTime.Modified(); // cache is up-to-date
     assert("check: positive_dim0" && this->NumberOf0DCells>=0);
     assert("check: valid_dim0" && this->NumberOf0DCells<=numCells);
     assert("check: positive_dim1" && this->NumberOf1DCells>=0);
     assert("check: valid_dim1" && this->NumberOf1DCells<=numCells);
     assert("check: positive_dim2" && this->NumberOf2DCells>=0);
     assert("check: valid_dim2" && this->NumberOf2DCells<=numCells);
     assert("check: positive_dim3" && this->NumberOf3DCells>=0);
     assert("check: valid_dim3" && this->NumberOf3DCells<=numCells);
     }
}

//----------------------------------------------------------------------------
// Description:
// Number of cells that explicitly define the dataset. See NewCellIterator
// for more details.
// \pre valid_dim_range: (dim>=-1) && (dim<=3)
// \post positive_result: result>=0
vtkIdType vtkBridgeDataSet::GetNumberOfCells(int dim)
{
  assert("pre: valid_dim_range" && (dim>=-1) && (dim<=3));

  vtkIdType result=0;
  if(this->Implementation!=0)
    {
    if(dim==-1)
      {
      result=this->Implementation->GetNumberOfCells();
      }
    else
      {
      this->ComputeNumberOfCellsAndTypes();
      switch(dim)
        {
        case 0:
          result=this->NumberOf0DCells;
          break;
        case 1:
          result=this->NumberOf1DCells;
          break;
        case 2:
          result=this->NumberOf2DCells;
          break;
        case 3:
          result=this->NumberOf3DCells;
          break;
        }
      }
    }

  assert("post: positive_result" && result>=0);
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Return -1 if the dataset is explicitly defined by cells of several
// dimensions or if there is no cell. If the dataset is explicitly defined by
// cells of a unique dimension, return this dimension.
// \post valid_range: (result>=-1) && (result<=3)
int vtkBridgeDataSet::GetCellDimension()
{
  int result=0;
  int accu=0;

  this->ComputeNumberOfCellsAndTypes();

  if(this->NumberOf0DCells!=0)
    {
    accu++;
    result=0;
    }
  if(this->NumberOf1DCells!=0)
    {
    accu++;
    result=1;
    }
  if(this->NumberOf2DCells!=0)
    {
    accu++;
    result=2;
    }
  if(this->NumberOf3DCells!=0)
    {
    accu++;
    result=3;
    }
  if(accu!=1) // no cells at all or several dimensions
    {
    result=-1;
    }
  assert("post: valid_range" && (result>=-1) && (result<=3));
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Get a list of types of cells in a dataset. The list consists of an array
// of types (not necessarily in any order), with a single entry per type.
// For example a dataset 5 triangles, 3 lines, and 100 hexahedra would
// result a list of three entries, corresponding to the types VTK_TRIANGLE,
// VTK_LINE, and VTK_HEXAHEDRON.
// THIS METHOD IS THREAD SAFE IF FIRST CALLED FROM A SINGLE THREAD AND
// THE DATASET IS NOT MODIFIED
// \pre types_exist: types!=0
void vtkBridgeDataSet::GetCellTypes(vtkCellTypes *types)
{
  assert("pre: types_exist" && types!=0);

  int i;
  int c;
  this->ComputeNumberOfCellsAndTypes();

  // copy from `this->Types' to `types'.
  types->Reset();
  c=this->Types->GetNumberOfTypes();
  i=0;
  while(i<c)
    {
    types->InsertNextType(this->Types->GetCellType(i));
    ++i;
    }
}

//----------------------------------------------------------------------------
// Description:
// Cells of dimension `dim' (or all dimensions if -1) that explicitly define
// the dataset. For instance, it will return only tetrahedra if the mesh is
// defined by tetrahedra. If the mesh is composed of two parts, one with
// tetrahedra and another part with triangles, it will return both, but will
// not return edges and vertices.
// \pre valid_dim_range: (dim>=-1) && (dim<=3)
// \post result_exists: result!=0
vtkGenericCellIterator *vtkBridgeDataSet::NewCellIterator(int dim)
{
  assert("pre: valid_dim_range" && (dim>=-1) && (dim<=3));

  vtkBridgeCellIterator *result=vtkBridgeCellIterator::New();
  result->InitWithDataSet(this,dim); // vtkBridgeCellIteratorOnDataSetCells

  assert("post: result_exists" && result!=0);
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Boundaries of dimension `dim' (or all dimensions if -1) of the dataset.
// If `exteriorOnly' is true, only  the exterior boundaries of the dataset
// will be returned, otherwise it will return exterior and interior
// boundaries.
// \pre valid_dim_range: (dim>=-1) && (dim<=2)
// \post result_exists: result!=0
vtkGenericCellIterator *vtkBridgeDataSet::NewBoundaryIterator(int dim,
                                                       int exteriorOnly)
{
  assert("pre: valid_dim_range" && (dim>=-1) && (dim<=2));

  vtkBridgeCellIterator *result=vtkBridgeCellIterator::New();
  result->InitWithDataSetBoundaries(this,dim,exteriorOnly); //vtkBridgeCellIteratorOnDataSetBoundaries(dim,exterior_only);

  assert("post: result_exists" && result!=0);
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Points composing the dataset; they can be on a vertex or isolated.
// \post result_exists: result!=0
vtkGenericPointIterator *vtkBridgeDataSet::NewPointIterator()
{
  vtkBridgePointIterator *result=vtkBridgePointIterator::New();
  result->InitWithDataSet(this);
  assert("post: result_exists" && result!=0);
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Estimated size needed after tessellation (or special operation)
vtkIdType vtkBridgeDataSet::GetEstimatedSize()
{
  return this->GetNumberOfPoints()*this->GetNumberOfCells();
}

//----------------------------------------------------------------------------
// Description:
// Locate closest cell to position `x' (global coordinates) with respect to
// a tolerance squared `tol2' and an initial guess `cell' (if valid). The
// result consists in the `cell', the `subId' of the sub-cell (0 if primary
// cell), the parametric coordinates `pcoord' of the position. It returns
// whether the position is inside the cell or not. Tolerance is used to
// control how close the point is to be considered "in" the cell.
// THIS METHOD IS NOT THREAD SAFE.
// \pre not_empty: GetNumberOfCells()>0
// \pre cell_exists: cell!=0
// \pre positive_tolerance: tol2>0
// \post clamped_pcoords: result implies (0<=pcoords[0]<=1 && )

int vtkBridgeDataSet::FindCell(double x[3],
                               vtkGenericCellIterator* &cell,
                               double tol2,
                               int &subId,
                               double pcoords[3])
{
  assert("pre: not_empty" && GetNumberOfCells()>0);
  assert("pre: cell_exists" && cell!=0);
  assert("pre: positive_tolerance" && tol2>0);

  vtkIdType cellid;
  vtkBridgeCell *c;
  vtkBridgeCellIterator *it=static_cast<vtkBridgeCellIterator *>(cell);
  vtkCell *c2;

  double *ignoredWeights=new double[this->Implementation->GetMaxCellSize()];

  if(cell->IsAtEnd())
    {
    cellid=this->Implementation->FindCell(x,0,0,tol2,subId,pcoords,
                                          ignoredWeights);
    }
  else
    {
    c=static_cast<vtkBridgeCell *>(cell->GetCell());
    c2=c->Cell; // bridge
    cellid=c->GetId(); // adaptor
    cellid=this->Implementation->FindCell(x,c2,cellid,tol2,subId,pcoords,
                                          ignoredWeights);
    }
  delete [] ignoredWeights;
  if(cellid>=0)
    {
    it->InitWithOneCell(this,cellid); // at end
    it->Begin();
    // clamp:
    int i=0;
    while(i<3)
      {
      if(pcoords[i]<0)
        {
        pcoords[i]=0;
        }
      else if(pcoords[i]>1)
        {
        pcoords[i]=1;
        }
      ++i;
      }
    }

  // A=>B: !A || B
  // result => clamped pcoords
  assert("post: clamped_pcoords" && ((cellid<0)||(pcoords[0]>=0
                                                 && pcoords[0]<=1
                                                 && pcoords[1]>=0
                                                 && pcoords[1]<=1
                                                 && pcoords[2]>=0
                                                 && pcoords[2]<=1)));

  return cellid>=0; // bool
}

//----------------------------------------------------------------------------
// Description:
// Locate closest point `p' to position `x' (global coordinates)
// \pre not_empty: GetNumberOfPoints()>0
// \pre p_exists: p!=0
void vtkBridgeDataSet::FindPoint(double x[3],
                                 vtkGenericPointIterator *p)
{
  assert("pre: not_empty" && GetNumberOfPoints()>0);
  assert("pre: p_exists" && p!=0);
  vtkBridgePointIterator *bp=static_cast<vtkBridgePointIterator *>(p);

  if(this->Implementation!=0)
    {
    vtkIdType pt=this->Implementation->FindPoint(x);
    bp->InitWithOnePoint(this,pt);
    }
  else
    {
    bp->InitWithOnePoint(this,-1);
    }
}

//----------------------------------------------------------------------------
// Description:
// Datasets are composite objects and need to check each part for MTime.
unsigned long int vtkBridgeDataSet::GetMTime()
{
  unsigned long result;
  unsigned long mtime;

  result = this->Superclass::GetMTime();

  if(this->Implementation!=0)
    {
    mtime = this->Implementation->GetMTime();
    result = ( mtime > result ? mtime : result );
    }
  return result;
}

//----------------------------------------------------------------------------
// Description:
// Compute the geometry bounding box.
void vtkBridgeDataSet::ComputeBounds()
{
  double *bounds;

  if ( this->GetMTime() > this->ComputeTime )
    {
    if(this->Implementation!=0)
      {
      this->Implementation->ComputeBounds();
      this->ComputeTime.Modified();
      bounds=this->Implementation->GetBounds();
      memcpy(this->Bounds,bounds,sizeof(double)*6);
      }
    else
      {
      vtkMath::UninitializeBounds(this->Bounds);
      }
    this->ComputeTime.Modified();
    }
}
