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

  Program:   Visualization Toolkit
  Module:    vtkDataSetSurfaceFilter.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   vtkDataSetSurfaceFilter
 * @brief   Extracts outer (polygonal) surface.
 *
 * vtkDataSetSurfaceFilter is a faster version of vtkGeometry filter, but it
 * does not have an option to select bounds.  It may use more memory than
 * vtkGeometryFilter.  It only has one option: whether to use triangle strips
 * when the input type is structured.
 *
 * @sa
 * vtkGeometryFilter vtkStructuredGridGeometryFilter.
 */

#ifndef vtkDataSetSurfaceFilter_h
#define vtkDataSetSurfaceFilter_h

#include "vtkFiltersGeometryModule.h" // For export macro
#include "vtkPolyDataAlgorithm.h"

class vtkPointData;
class vtkPoints;
class vtkIdTypeArray;
class vtkStructuredGrid;

// Helper structure for hashing faces.
struct vtkFastGeomQuadStruct
{
  struct vtkFastGeomQuadStruct* Next;
  vtkIdType SourceId;
  int numPts;
  vtkIdType* ptArray;
};
typedef struct vtkFastGeomQuadStruct vtkFastGeomQuad;

class VTKFILTERSGEOMETRY_EXPORT vtkDataSetSurfaceFilter : public vtkPolyDataAlgorithm
{
public:
  static vtkDataSetSurfaceFilter* New();
  vtkTypeMacro(vtkDataSetSurfaceFilter, vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  //@{
  /**
   * When input is structured data, this flag will generate faces with
   * triangle strips.  This should render faster and use less memory, but no
   * cell data is copied.  By default, UseStrips is Off.
   */
  vtkSetMacro(UseStrips, vtkTypeBool);
  vtkGetMacro(UseStrips, vtkTypeBool);
  vtkBooleanMacro(UseStrips, vtkTypeBool);
  //@}

  //@{
  /**
   * If PieceInvariant is true, vtkDataSetSurfaceFilter requests
   * 1 ghost level from input in order to remove internal surface
   * that are between processes. False by default.
   */
  vtkSetMacro(PieceInvariant, int);
  vtkGetMacro(PieceInvariant, int);
  //@}

  //@{
  /**
   * If on, the output polygonal dataset will have a celldata array that
   * holds the cell index of the original 3D cell that produced each output
   * cell. This is useful for cell picking. The default is off to conserve
   * memory. Note that PassThroughCellIds will be ignored if UseStrips is on,
   * since in that case each tringle strip can represent more than on of the
   * input cells.
   */
  vtkSetMacro(PassThroughCellIds, vtkTypeBool);
  vtkGetMacro(PassThroughCellIds, vtkTypeBool);
  vtkBooleanMacro(PassThroughCellIds, vtkTypeBool);
  vtkSetMacro(PassThroughPointIds, vtkTypeBool);
  vtkGetMacro(PassThroughPointIds, vtkTypeBool);
  vtkBooleanMacro(PassThroughPointIds, vtkTypeBool);
  //@}

  //@{
  /**
   * If PassThroughCellIds or PassThroughPointIds is on, then these ivars
   * control the name given to the field in which the ids are written into.  If
   * set to nullptr, then vtkOriginalCellIds or vtkOriginalPointIds (the default)
   * is used, respectively.
   */
  vtkSetStringMacro(OriginalCellIdsName);
  virtual const char* GetOriginalCellIdsName()
  {
    return (this->OriginalCellIdsName ? this->OriginalCellIdsName : "vtkOriginalCellIds");
  }
  vtkSetStringMacro(OriginalPointIdsName);
  virtual const char* GetOriginalPointIdsName()
  {
    return (this->OriginalPointIdsName ? this->OriginalPointIdsName : "vtkOriginalPointIds");
  }
  //@}

  //@{
  /**
   * If the input is an unstructured grid with nonlinear faces, this parameter
   * determines how many times the face is subdivided into linear faces.  If 0,
   * the output is the equivalent of its linear counterpart (and the midpoints
   * determining the nonlinear interpolation are discarded).  If 1 (the
   * default), the nonlinear face is triangulated based on the midpoints.  If
   * greater than 1, the triangulated pieces are recursively subdivided to reach
   * the desired subdivision.  Setting the value to greater than 1 may cause
   * some point data to not be passed even if no nonlinear faces exist.  This
   * option has no effect if the input is not an unstructured grid.
   */
  vtkSetMacro(NonlinearSubdivisionLevel, int);
  vtkGetMacro(NonlinearSubdivisionLevel, int);
  //@}

  //@{
  /**
   * Direct access methods that can be used to use the this class as an
   * algorithm without using it as a filter.
   */
  virtual int StructuredExecute(
    vtkDataSet* input, vtkPolyData* output, vtkIdType* ext, vtkIdType* wholeExt);
#ifdef VTK_USE_64BIT_IDS
  virtual int StructuredExecute(vtkDataSet* input, vtkPolyData* output, int* ext32, int* wholeExt32)
  {
    vtkIdType ext[6];
    vtkIdType wholeExt[6];
    for (int cc = 0; cc < 6; cc++)
    {
      ext[cc] = ext32[cc];
      wholeExt[cc] = wholeExt32[cc];
    }
    return this->StructuredExecute(input, output, ext, wholeExt);
  }
#endif
  virtual int UnstructuredGridExecute(vtkDataSet* input, vtkPolyData* output);
  virtual int DataSetExecute(vtkDataSet* input, vtkPolyData* output);
  virtual int StructuredWithBlankingExecute(vtkStructuredGrid* input, vtkPolyData* output);
  virtual int UniformGridExecute(vtkDataSet* input, vtkPolyData* output, vtkIdType* ext,
    vtkIdType* wholeExt, bool extractface[6]);
#ifdef VTK_USE_64BIT_IDS
  virtual int UniformGridExecute(
    vtkDataSet* input, vtkPolyData* output, int* ext32, int* wholeExt32, bool extractface[6])
  {
    vtkIdType ext[6];
    vtkIdType wholeExt[6];
    for (int cc = 0; cc < 6; cc++)
    {
      ext[cc] = ext32[cc];
      wholeExt[cc] = wholeExt32[cc];
    }
    return this->UniformGridExecute(input, output, ext, wholeExt, extractface);
  }
#endif
  //@}

protected:
  vtkDataSetSurfaceFilter();
  ~vtkDataSetSurfaceFilter() override;

  vtkTypeBool UseStrips;

  int RequestUpdateExtent(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;

  int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
  int FillInputPortInformation(int port, vtkInformation* info) override;

  // Helper methods.

  /**
   * Estimates the total number of points & cells on the surface to render
   * ext -- the extent of the structured data in question (in)
   * wholeExt -- the global extent of the structured data (in)
   * numPoints -- the estimated number of points (out)
   * numCells -- the estimated number of cells (out)
   */
  void EstimateStructuredDataArraySizes(
    vtkIdType* ext, vtkIdType* wholeExt, vtkIdType& numPoints, vtkIdType& numCells);

  void ExecuteFaceStrips(vtkDataSet* input, vtkPolyData* output, int maxFlag, vtkIdType* ext,
    int aAxis, int bAxis, int cAxis, vtkIdType* wholeExt);

  void ExecuteFaceQuads(vtkDataSet* input, vtkPolyData* output, int maxFlag, vtkIdType* ext,
    int aAxis, int bAxis, int cAxis, vtkIdType* wholeExt, bool checkVisibility);

  void ExecuteFaceQuads(vtkDataSet* input, vtkPolyData* output, int maxFlag, vtkIdType* ext,
    int aAxis, int bAxis, int cAxis, vtkIdType* wholeExt);

  void InitializeQuadHash(vtkIdType numPoints);
  void DeleteQuadHash();
  virtual void InsertQuadInHash(
    vtkIdType a, vtkIdType b, vtkIdType c, vtkIdType d, vtkIdType sourceId);
  virtual void InsertTriInHash(
    vtkIdType a, vtkIdType b, vtkIdType c, vtkIdType sourceId, vtkIdType faceId = -1);
  virtual void InsertPolygonInHash(const vtkIdType* ids, int numpts, vtkIdType sourceId);
  void InitQuadHashTraversal();
  vtkFastGeomQuad* GetNextVisibleQuadFromHash();

  vtkFastGeomQuad** QuadHash;
  vtkIdType QuadHashLength;
  vtkFastGeomQuad* QuadHashTraversal;
  vtkIdType QuadHashTraversalIndex;

  vtkIdType* PointMap;
  vtkIdType GetOutputPointId(
    vtkIdType inPtId, vtkDataSet* input, vtkPoints* outPts, vtkPointData* outPD);

  class vtkEdgeInterpolationMap;

  vtkEdgeInterpolationMap* EdgeMap;
  vtkIdType GetInterpolatedPointId(vtkIdType edgePtA, vtkIdType edgePtB, vtkDataSet* input,
    vtkCell* cell, double pcoords[3], vtkPoints* outPts, vtkPointData* outPD);

  vtkIdType NumberOfNewCells;

  // Better memory allocation for faces (hash)
  void InitFastGeomQuadAllocation(vtkIdType numberOfCells);
  vtkFastGeomQuad* NewFastGeomQuad(int numPts);
  void DeleteAllFastGeomQuads();
  // -----
  vtkIdType FastGeomQuadArrayLength;
  vtkIdType NumberOfFastGeomQuadArrays;
  unsigned char** FastGeomQuadArrays; // store this data as an array of bytes
  // These indexes allow us to find the next available face.
  vtkIdType NextArrayIndex;
  vtkIdType NextQuadIndex;

  int PieceInvariant;

  vtkTypeBool PassThroughCellIds;
  void RecordOrigCellId(vtkIdType newIndex, vtkIdType origId);
  virtual void RecordOrigCellId(vtkIdType newIndex, vtkFastGeomQuad* quad);
  vtkIdTypeArray* OriginalCellIds;
  char* OriginalCellIdsName;

  vtkTypeBool PassThroughPointIds;
  void RecordOrigPointId(vtkIdType newIndex, vtkIdType origId);
  vtkIdTypeArray* OriginalPointIds;
  char* OriginalPointIdsName;

  int NonlinearSubdivisionLevel;

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

#endif
