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

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkCellArray.h,v $

  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 vtkCellArray - object to represent cell connectivity
// .SECTION Description
// vtkCellArray is a supporting object that explicitly represents cell 
// connectivity. The cell array structure is a raw integer list
// of the form: (n,id1,id2,...,idn, n,id1,id2,...,idn, ...)
// where n is the number of points in the cell, and id is a zero-offset index 
// into an associated point list.
//
// Advantages of this data structure are its compactness, simplicity, and 
// easy interface to external data.  However, it is totally inadequate for 
// random access.  This functionality (when necessary) is accomplished by 
// using the vtkCellTypes and vtkCellLinks objects to extend the definition of 
// the data structure.
//
// .SECTION See Also
// vtkCellTypes vtkCellLinks

#ifndef __vtkCellArray_h
#define __vtkCellArray_h

#include "vtkObject.h"

#include "vtkIdTypeArray.h" // Needed for inline methods
#include "vtkCell.h" // Needed for inline methods

class VTK_FILTERING_EXPORT vtkCellArray : public vtkObject
{
public:
  vtkTypeRevisionMacro(vtkCellArray,vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent);

  // Description:
  // Instantiate cell array (connectivity list).
  static vtkCellArray *New();

  // Description:
  // Allocate memory and set the size to extend by.
  int Allocate(const vtkIdType sz, const int ext=1000) 
    {return this->Ia->Allocate(sz,ext);}

  // Description:
  // Free any memory and reset to an empty state.
  void Initialize() 
    {this->Ia->Initialize();}

  // Description:
  // Get the number of cells in the array.
  vtkGetMacro(NumberOfCells, vtkIdType);

  // Description:
  // Set the number of cells in the array.
  // DO NOT do any kind of allocation, advanced use only.
  vtkSetMacro(NumberOfCells, vtkIdType);

  // Description:
  // Utility routines help manage memory of cell array. EstimateSize()
  // returns a value used to initialize and allocate memory for array based
  // on number of cells and maximum number of points making up cell.  If 
  // every cell is the same size (in terms of number of points), then the 
  // memory estimate is guaranteed exact. (If not exact, use Squeeze() to
  // reclaim any extra memory.)
  vtkIdType EstimateSize(vtkIdType numCells, int maxPtsPerCell)
    {return numCells*(1+maxPtsPerCell);}

  // Description:
  // A cell traversal methods that is more efficient than vtkDataSet traversal
  // methods.  InitTraversal() initializes the traversal of the list of cells.
  void InitTraversal() {this->TraversalLocation=0;};

  // Description:
  // A cell traversal methods that is more efficient than vtkDataSet traversal
  // methods.  GetNextCell() gets the next cell in the list. If end of list
  // is encountered, 0 is returned.
  int GetNextCell(vtkIdType& npts, vtkIdType* &pts);

  // Description:
  // Get the size of the allocated connectivity array.
  vtkIdType GetSize() 
    {return this->Ia->GetSize();}
  
  // Description:
  // Get the total number of entries (i.e., data values) in the connectivity 
  // array. This may be much less than the allocated size (i.e., return value 
  // from GetSize().)
  vtkIdType GetNumberOfConnectivityEntries() 
    {return this->Ia->GetMaxId()+1;}

  // Description:
  // Internal method used to retrieve a cell given an offset into
  // the internal array.
  void GetCell(vtkIdType loc, vtkIdType &npts, vtkIdType* &pts);

  // Description:
  // Insert a cell object. Return the cell id of the cell.
  vtkIdType InsertNextCell(vtkCell *cell);

  // Description:
  // Create a cell by specifying the number of points and an array of point
  // id's.  Return the cell id of the cell.
  vtkIdType InsertNextCell(vtkIdType npts, const vtkIdType* pts);

  // Description:
  // Create a cell by specifying a list of point ids. Return the cell id of
  // the cell.
  vtkIdType InsertNextCell(vtkIdList *pts);

  // Description:
  // Create cells by specifying count, and then adding points one at a time
  // using method InsertCellPoint(). If you don't know the count initially,
  // use the method UpdateCellCount() to complete the cell. Return the cell
  // id of the cell.
  vtkIdType InsertNextCell(int npts);

  // Description:
  // Used in conjunction with InsertNextCell(int npts) to add another point
  // to the list of cells.
  void InsertCellPoint(vtkIdType id);

  // Description:
  // Used in conjunction with InsertNextCell(int npts) and InsertCellPoint() to
  // update the number of points defining the cell.
  void UpdateCellCount(int npts);

  // Description:
  // Computes the current insertion location within the internal array. 
  // Used in conjunction with GetCell(int loc,...).
  vtkIdType GetInsertLocation(int npts)
    {return (this->InsertLocation - npts - 1);};
  
  // Description:
  // Get/Set the current traversal location.
  vtkIdType GetTraversalLocation() 
    {return this->TraversalLocation;}
  void SetTraversalLocation(vtkIdType loc) 
    {this->TraversalLocation = loc;}
  
  // Description:
  // Computes the current traversal location within the internal array. Used 
  // in conjunction with GetCell(int loc,...).
  vtkIdType GetTraversalLocation(vtkIdType npts) 
    {return(this->TraversalLocation-npts-1);}
  
  // Description:
  // Special method inverts ordering of current cell. Must be called
  // carefully or the cell topology may be corrupted.
  void ReverseCell(vtkIdType loc);

  // Description:
  // Replace the point ids of the cell with a different list of point ids.
  void ReplaceCell(vtkIdType loc, int npts, const vtkIdType *pts);

  // Description:
  // Returns the size of the largest cell. The size is the number of points
  // defining the cell.
  int GetMaxCellSize();

  // Description:
  // Get pointer to array of cell data.
  vtkIdType *GetPointer() 
    {return this->Ia->GetPointer(0);}

  // Description:
  // Get pointer to data array for purpose of direct writes of data. Size is the
  // total storage consumed by the cell array. ncells is the number of cells
  // represented in the array.
  vtkIdType *WritePointer(const vtkIdType ncells, const vtkIdType size);

  // Description:
  // Define multiple cells by providing a connectivity list. The list is in
  // the form (npts,p0,p1,...p(npts-1), repeated for each cell). Be careful
  // using this method because it discards the old cells, and anything
  // referring these cells becomes invalid (for example, if BuildCells() has
  // been called see vtkPolyData).  The traversal location is reset to the
  // beginning of the list; the insertion location is set to the end of the
  // list.
  void SetCells(vtkIdType ncells, vtkIdTypeArray *cells);
  
  // Description:
  // Perform a deep copy (no reference counting) of the given cell array.
  void DeepCopy(vtkCellArray *ca);

  // Description:
  // Return the underlying data as a data array.
  vtkIdTypeArray* GetData() 
    {return this->Ia;}

  // Description:
  // Reuse list. Reset to initial condition.
  void Reset();

  // Description:
  // Reclaim any extra memory.
  void Squeeze() 
    {this->Ia->Squeeze();}

  // Description:
  // Return the memory in kilobytes consumed by this cell array. Used to
  // support streaming and reading/writing data. The value returned is
  // guaranteed to be greater than or equal to the memory required to
  // actually represent the data represented by this object. The 
  // information returned is valid only after the pipeline has 
  // been updated.
  unsigned long GetActualMemorySize();
  
protected:
  vtkCellArray();
  ~vtkCellArray();

  vtkIdType NumberOfCells;
  vtkIdType InsertLocation;     //keep track of current insertion point
  vtkIdType TraversalLocation;   //keep track of traversal position
  vtkIdTypeArray *Ia;

private:
  vtkCellArray(const vtkCellArray&);  // Not implemented.
  void operator=(const vtkCellArray&);  // Not implemented.
};


//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkIdType npts,
                                              const vtkIdType* pts)
{
  vtkIdType i = this->Ia->GetMaxId() + 1;
  vtkIdType *ptr = this->Ia->WritePointer(i, npts+1);
  
  for ( *ptr++ = npts, i = 0; i < npts; i++)
    {
    *ptr++ = *pts++;
    }

  this->NumberOfCells++;
  this->InsertLocation += npts + 1;

  return this->NumberOfCells - 1;
}

//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkIdList *pts)
{
  vtkIdType npts = pts->GetNumberOfIds();
  vtkIdType i = this->Ia->GetMaxId() + 1;
  vtkIdType *ptr = this->Ia->WritePointer(i,npts+1);
  
  for ( *ptr++ = npts, i = 0; i < npts; i++)
    {
    *ptr++ = pts->GetId(i);
    }

  this->NumberOfCells++;
  this->InsertLocation += npts + 1;

  return this->NumberOfCells - 1;
}

//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(int npts)
{
  this->InsertLocation = this->Ia->InsertNextValue(npts) + 1;
  this->NumberOfCells++;

  return this->NumberOfCells - 1;
}

//----------------------------------------------------------------------------
inline void vtkCellArray::InsertCellPoint(vtkIdType id) 
{
  this->Ia->InsertValue(this->InsertLocation++, id);
}

//----------------------------------------------------------------------------
inline void vtkCellArray::UpdateCellCount(int npts) 
{
  this->Ia->SetValue(this->InsertLocation-npts-1, npts);
}

//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkCell *cell)
{
  int npts = cell->GetNumberOfPoints();
  vtkIdType i = this->Ia->GetMaxId() + 1;
  vtkIdType *ptr = this->Ia->WritePointer(i,npts+1);
  
  for ( *ptr++ = npts, i = 0; i < npts; i++)
    {
    *ptr++ = cell->PointIds->GetId(i);
    }

  this->NumberOfCells++;
  this->InsertLocation += npts + 1;

  return this->NumberOfCells - 1;
}

//----------------------------------------------------------------------------
inline void vtkCellArray::Reset() 
{
  this->NumberOfCells = 0;
  this->InsertLocation = 0;
  this->TraversalLocation = 0;
  this->Ia->Reset();
}

//----------------------------------------------------------------------------
inline int vtkCellArray::GetNextCell(vtkIdType& npts, vtkIdType* &pts)
{
  if ( this->Ia->GetMaxId() >= 0 && 
       this->TraversalLocation <= this->Ia->GetMaxId() ) 
    {
    npts = this->Ia->GetValue(this->TraversalLocation++);
    pts = this->Ia->GetPointer(this->TraversalLocation);
    this->TraversalLocation += npts;
    return 1;
    }
  else
    {
    return 0;
    }
}

//----------------------------------------------------------------------------
inline void vtkCellArray::GetCell(vtkIdType loc, vtkIdType &npts,
                                  vtkIdType* &pts)
{
  npts = this->Ia->GetValue(loc++);
  pts  = this->Ia->GetPointer(loc);
}

//----------------------------------------------------------------------------
inline void vtkCellArray::ReverseCell(vtkIdType loc)
{
  int i;
  vtkIdType tmp;
  vtkIdType npts=this->Ia->GetValue(loc);
  vtkIdType *pts=this->Ia->GetPointer(loc+1);
  for (i=0; i < (npts/2); i++) 
    {
    tmp = pts[i];
    pts[i] = pts[npts-i-1];
    pts[npts-i-1] = tmp;
    }
}

//----------------------------------------------------------------------------
inline void vtkCellArray::ReplaceCell(vtkIdType loc, int npts,
                                      const vtkIdType *pts)
{
  vtkIdType *oldPts=this->Ia->GetPointer(loc+1);
  for (int i=0; i < npts; i++)
    {
    oldPts[i] = pts[i];
    }
}

//----------------------------------------------------------------------------
inline vtkIdType *vtkCellArray::WritePointer(const vtkIdType ncells,
                                             const vtkIdType size)
{
  this->NumberOfCells = ncells;
  this->InsertLocation = 0;
  this->TraversalLocation = 0;
  return this->Ia->WritePointer(0,size);
}

#endif
