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

  Program:   Visualization Toolkit
  Module:    vtkCellArray.h

  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.

=========================================================================*/
/**
 * @class   vtkCellArray
 * @brief   object to represent cell connectivity
 *
 * 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.
 *
 * @sa
 * vtkCellTypes vtkCellLinks
*/

#ifndef vtkCellArray_h
#define vtkCellArray_h

#include "vtkCommonDataModelModule.h" // For export macro
#include "vtkObject.h"

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

class VTKCOMMONDATAMODEL_EXPORT vtkCellArray : public vtkObject
{
public:
  vtkTypeMacro(vtkCellArray,vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

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

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

  /**
   * Free any memory and reset to an empty state.
   */
  void Initialize();

  //@{
  /**
   * Get the number of cells in the array.
   */
  vtkGetMacro(NumberOfCells, vtkIdType);
  //@}

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

  /**
   * 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);}

  /**
   * 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;};

  /**
   * 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. A value of 1 is returned whenever
   * npts and pts have been updated without error.
   */
  int GetNextCell(vtkIdType& npts, vtkIdType* &pts)
    VTK_SIZEHINT(pts, npts);

  /**
   * 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(vtkIdList *pts);

  /**
   * Get the size of the allocated connectivity array.
   */
  vtkIdType GetSize()
    {return this->Ia->GetSize();}

  /**
   * 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;}

  /**
   * Internal method used to retrieve a cell given an offset into
   * the internal array.
   */
  void GetCell(vtkIdType loc, vtkIdType &npts, vtkIdType* &pts)
    VTK_EXPECTS(0 <= loc && loc < GetSize())
    VTK_SIZEHINT(pts, npts);

  /**
   * Internal method used to retrieve a cell given an offset into
   * the internal array.
   */
  void GetCell(vtkIdType loc, vtkIdList* pts)
    VTK_EXPECTS(0 <= loc && loc < GetSize());

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

  /**
   * 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)
    VTK_SIZEHINT(pts, npts);

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

  /**
   * 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);

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

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

  /**
   * 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);};

  /**
   * Get/Set the current traversal location.
   */
  vtkIdType GetTraversalLocation()
    {return this->TraversalLocation;}
  void SetTraversalLocation(vtkIdType loc)
    {this->TraversalLocation = loc;}

  /**
   * 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);}

  /**
   * Special method inverts ordering of current cell. Must be called
   * carefully or the cell topology may be corrupted.
   */
  void ReverseCell(vtkIdType loc)
    VTK_EXPECTS(0 <= loc && loc < GetSize());

  /**
   * Replace the point ids of the cell with a different list of point ids.
   * Calling this method does not mark the vtkCellArray as modified.  This is
   * the responsibility of the caller and may be done after multiple calls to
   * ReplaceCell.
   */
  void ReplaceCell(vtkIdType loc, int npts, const vtkIdType *pts)
    VTK_EXPECTS(0 <= loc && loc < GetSize())
    VTK_SIZEHINT(pts, npts);

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

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

  /**
   * 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);

  /**
   * 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);

  /**
   * Perform a deep copy (no reference counting) of the given cell array.
   */
  void DeepCopy(vtkCellArray *ca);

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

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

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

  /**
   * Return the memory in kibibytes (1024 bytes) 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() override;

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

private:
  vtkCellArray(const vtkCellArray&) = delete;
  void operator=(const vtkCellArray&) = delete;
};


//----------------------------------------------------------------------------
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(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(vtkIdList *pts)
{
  return this->InsertNextCell(pts->GetNumberOfIds(), pts->GetPointer(0));
}

//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkCell *cell)
{
  return this->InsertNextCell(cell->GetNumberOfPoints(),
                              cell->PointIds->GetPointer(0));
}

//----------------------------------------------------------------------------
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;
  }
  npts=0;
  pts=nullptr;
  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
