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

  Program:   Visualization Toolkit
  Module:    vtkCellLocator.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   vtkCellLocator
 * @brief   octree-based spatial search object to quickly locate cells
 *
 * vtkCellLocator is a spatial search object to quickly locate cells in 3D.
 * vtkCellLocator uses a uniform-level octree subdivision, where each octant
 * (an octant is also referred to as a bucket) carries an indication of
 * whether it is empty or not, and each leaf octant carries a list of the
 * cells inside of it. (An octant is not empty if it has one or more cells
 * inside of it.)  Typical operations are intersection with a line to return
 * candidate cells, or intersection with another vtkCellLocator to return
 * candidate cells.
 *
 * @warning
 * Many other types of spatial locators have been developed, such as
 * variable depth octrees and kd-trees. These are often more efficient
 * for the operations described here. vtkCellLocator has been designed
 * for subclassing; so these locators can be derived if necessary.
 *
 * @warning
 * Most of the methods of this class are not thread-safe. For a thread-safe,
 * more efficient generic implementation, please use vtkStaticCellLocator
 *
 * @sa
 * vtkLocator vtkPointLocator vtkOBBTree vtkStaticCellLocator
 */

#ifndef vtkCellLocator_h
#define vtkCellLocator_h

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

class vtkNeighborCells;

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

  /**
   * Construct with automatic computation of divisions, averaging
   * 25 cells per bucket.
   */
  static vtkCellLocator* New();

  /**
   * Specify the average number of cells in each octant.
   */
  void SetNumberOfCellsPerBucket(int N) { this->SetNumberOfCellsPerNode(N); }
  int GetNumberOfCellsPerBucket() { return this->NumberOfCellsPerNode; }

  // Re-use any superclass signatures that we don't override.
  using vtkAbstractCellLocator::FindCell;
  using vtkAbstractCellLocator::FindClosestPoint;
  using vtkAbstractCellLocator::FindClosestPointWithinRadius;
  using vtkAbstractCellLocator::IntersectWithLine;

  /**
   * Return intersection point (if any) AND the cell which was intersected by
   * the finite line. The cell is returned as a cell id and as a generic
   * cell.  For other IntersectWithLine signatures, see
   * vtkAbstractCellLocator.
   * THIS METHOD IS NOT THREAD SAFE.
   */
  int IntersectWithLine(const double a0[3], const double a1[3], double tol, double& t, double x[3],
    double pcoords[3], int& subId, vtkIdType& cellId, vtkGenericCell* cell) override;

  /**
   * Return the closest point and the cell which is closest to the point x.
   * The closest point is somewhere on a cell, it need not be one of the
   * vertices of the cell.  This version takes in a vtkGenericCell
   * to avoid allocating and deallocating the cell.  This is much faster than
   * the version which does not take a *cell, especially when this function is
   * called many times in a row such as by a for loop, where the allocation and
   * deallocation can be done only once outside the for loop.  If a cell is
   * found, "cell" contains the points and ptIds for the cell "cellId" upon
   * exit.
   * THIS METHOD IS NOT THREAD SAFE.
   */
  void FindClosestPoint(const double x[3], double closestPoint[3], vtkGenericCell* cell,
    vtkIdType& cellId, int& subId, double& dist2) override;

  /**
   * Return the closest point within a specified radius and the cell which is
   * closest to the point x. The closest point is somewhere on a cell, it
   * need not be one of the vertices of the cell. This method returns 1 if a
   * point is found within the specified radius. If there are no cells within
   * the specified radius, the method returns 0 and the values of
   * closestPoint, cellId, subId, and dist2 are undefined. This version takes
   * in a vtkGenericCell to avoid allocating and deallocating the cell.  This
   * is much faster than the version which does not take a *cell, especially
   * when this function is called many times in a row such as by a for loop,
   * where the allocation and dealloction can be done only once outside the
   * for loop.  If a closest point is found, "cell" contains the points and
   * ptIds for the cell "cellId" upon exit.  If a closest point is found,
   * inside returns the return value of the EvaluatePosition call to the
   * closest cell; inside(=1) or outside(=0). For other
   * FindClosestPointWithinRadius signatures, see vtkAbstractCellLocator.
   * THIS METHOD IS NOT THREAD SAFE.
   */
  vtkIdType FindClosestPointWithinRadius(double x[3], double radius, double closestPoint[3],
    vtkGenericCell* cell, vtkIdType& cellId, int& subId, double& dist2, int& inside) override;

  /**
   * Get the cells in a particular bucket.
   */
  virtual vtkIdList* GetCells(int bucket);

  /**
   * Return number of buckets available. Insure that the locator has been
   * built before attempting to access buckets (octants).
   */
  virtual int GetNumberOfBuckets(void);

  /**
   * Find the cell containing a given point. returns -1 if no cell found
   * the cell parameters are copied into the supplied variables, a cell must
   * be provided to store the information.
   */
  vtkIdType FindCell(
    double x[3], double tol2, vtkGenericCell* GenCell, double pcoords[3], double* weights) override;

  /**
   * Return a list of unique cell ids inside of a given bounding box. The
   * user must provide the vtkIdList to populate. This method returns data
   * only after the locator has been built.
   */
  void FindCellsWithinBounds(double* bbox, vtkIdList* cells) override;

  /**
   * Given a finite line defined by the two points (p1,p2), return the list
   * of unique cell ids in the buckets containing the line. It is possible
   * that an empty cell list is returned. The user must provide the vtkIdList
   * to populate. This method returns data only after the locator has been
   * built.
   * THIS METHOD IS NOT THREAD SAFE.
   */
  void FindCellsAlongLine(
    const double p1[3], const double p2[3], double tolerance, vtkIdList* cells) override;

  //@{
  /**
   * Satisfy vtkLocator abstract interface.
   */
  void FreeSearchStructure() override;
  void BuildLocator() override;
  virtual void BuildLocatorIfNeeded();
  virtual void ForceBuildLocator();
  virtual void BuildLocatorInternal();
  void GenerateRepresentation(int level, vtkPolyData* pd) override;
  //@}

protected:
  vtkCellLocator();
  ~vtkCellLocator() override;

  void GetBucketNeighbors(int ijk[3], int ndivs, int level);
  void GetOverlappingBuckets(
    const double x[3], int ijk[3], double dist, int prevMinLevel[3], int prevMaxLevel[3]);

  void ClearCellHasBeenVisited();
  void ClearCellHasBeenVisited(vtkIdType id);

  double Distance2ToBucket(const double x[3], int nei[3]);
  double Distance2ToBounds(const double x[3], double bounds[6]);

  int NumberOfOctants;   // number of octants in tree
  double Bounds[6];      // bounding box root octant
  int NumberOfParents;   // number of parent octants
  double H[3];           // width of leaf octant in x-y-z directions
  int NumberOfDivisions; // number of "leaf" octant sub-divisions
  vtkIdList** Tree;      // octree

  void MarkParents(void*, int, int, int, int, int);
  void GetChildren(int idx, int level, int children[8]);
  int GenerateIndex(int offset, int numDivs, int i, int j, int k, vtkIdType& idx);
  void GenerateFace(
    int face, int numDivs, int i, int j, int k, vtkPoints* pts, vtkCellArray* polys);

  vtkNeighborCells* Buckets;
  unsigned char* CellHasBeenVisited;
  unsigned char QueryNumber;

  void ComputeOctantBounds(int i, int j, int k);
  double OctantBounds[6]; // the bounds of the current octant
  int IsInOctantBounds(const double x[3], double tol = 0.0)
  {
    if (this->OctantBounds[0] - tol <= x[0] && x[0] <= this->OctantBounds[1] + tol &&
      this->OctantBounds[2] - tol <= x[1] && x[1] <= this->OctantBounds[3] + tol &&
      this->OctantBounds[4] - tol <= x[2] && x[2] <= this->OctantBounds[5] + tol)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }

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

#endif
