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

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

=========================================================================*/
/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/

/**
 * @class   vtkOctreePointLocatorNode
 * @brief   Octree node that has 8 children each of equal size
 *
 *
 * This class represents a single spatial region in a 3D axis octant
 * partitioning.  It is intended to work efficiently with the
 * vtkOctreePointLocator and is not meant for general use.  It is assumed
 * the region bounds some set of points.  The ordering of the children is
 * (-x,-y,-z),(+x,-y,-z),(-x,+y,-z),(+x,+y,-z),(-x,-y,+z),(+x,-y,+z),
 * (-x,+y,+z),(+x,+y,+z).  The portion of the domain assigned to an
 * octant is Min < x <= Max.
 *
 * @sa
 * vtkOctreePointLocator
*/

#ifndef vtkOctreePointLocatorNode_h
#define vtkOctreePointLocatorNode_h

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

class vtkCell;
class vtkPlanesIntersection;

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

  static vtkOctreePointLocatorNode *New();

  //@{
  /**
   * Set/Get the number of points contained in this region.
   */
  void SetNumberOfPoints(int numberOfPoints)
  {
    this->NumberOfPoints = numberOfPoints;
  }
  vtkGetMacro(NumberOfPoints, int);
  //@}

  //@{
  /**
   * Set/Get the bounds of the spatial region represented by this node.
   * Caller allocates storage for 6-vector in GetBounds.
   */
  void SetBounds(double xMin, double xMax, double yMin,
                 double yMax, double zMin, double zMax);
  void SetBounds(const double b[6])
  {
    this->SetBounds(b[0], b[1], b[2], b[3], b[4], b[5]);
  }
  void GetBounds(double *b) const;
  //@}

  //@{
  /**
   * Set/Get the bounds of the points contained in this spatial region.
   * This may be smaller than the bounds of the region itself.
   * Caller allocates storage for 6-vector in GetDataBounds.
   */
  void SetDataBounds(double xMin, double xMax, double yMin,
                     double yMax, double zMin, double zMax);
  void GetDataBounds(double *b) const;
  //@}

  //@{
  /**
   * Get a pointer to the 3 bound minima (xmin, ymin and zmin) or the
   * 3 bound maxima (xmax, ymax, zmax).  Don't free this pointer.
   */
  vtkGetMacro(MinBounds, double*);
  vtkGetMacro(MaxBounds, double*);
  //@}

  //@{
  /**
   * Set the xmin, ymin and zmin value of the bounds of this region
   */
  void SetMinBounds(double minBounds[3])
  {
    this->MinBounds[0] = minBounds[0];
    this->MinBounds[1] = minBounds[1];
    this->MinBounds[2] = minBounds[2];
  }
  //@}

  //@{
  /**
   * Set the xmax, ymax and zmax value of the bounds of this region
   */
  void SetMaxBounds(double maxBounds[3])
  {
    this->MaxBounds[0] = maxBounds[0];
    this->MaxBounds[1] = maxBounds[1];
    this->MaxBounds[2] = maxBounds[2];
  }
  //@}

  //@{
  /**
   * Get a pointer to the 3 data bound minima (xmin, ymin and zmin) or the
   * 3 data bound maxima (xmax, ymax, zmax).  Don't free this pointer.
   */
  vtkGetMacro(MinDataBounds, double*);
  vtkGetMacro(MaxDataBounds, double*);
  //@}

  //@{
  /**
   * Set the xmin, ymin and zmin value of the bounds of this
   * data within this region.
   */
  void SetMinDataBounds(double minDataBounds[3])
  {
    this->MinDataBounds[0] = minDataBounds[0];
    this->MinDataBounds[1] = minDataBounds[1];
    this->MinDataBounds[2] = minDataBounds[2];
  }
  //@}

  //@{
  /**
   * Set the xmax, ymax and zmax value of the bounds of this
   * data within this region.
   */
  void SetMaxDataBounds(double maxDataBounds[3])
  {
    this->MaxDataBounds[0] = maxDataBounds[0];
    this->MaxDataBounds[1] = maxDataBounds[1];
    this->MaxDataBounds[2] = maxDataBounds[2];
  }
  //@}

  //@{
  /**
   * Get the ID associated with the region described by this node.  If
   * this is not a leaf node, this value should be -1.
   */
  vtkGetMacro(ID, int);
  //@}

  //@{
  /**
   * If this node is not a leaf node, there are leaf nodes below it whose
   * regions represent a partitioning of this region.  The IDs of these
   * leaf nodes form a contiguous set.  Get the first of the first point's
   * ID that is contained in this node.
   */
  vtkGetMacro(MinID, int);
  //@}

  /**
   * Add the 8 children.
   */
  void CreateChildNodes();

  /**
   * Delete the 8 children.
   */
  void DeleteChildNodes();

  /**
   * Get a pointer to the ith child of this node.
   */
  vtkOctreePointLocatorNode* GetChild(int i);

  /**
   * A vtkPlanesIntersection object represents a convex 3D region bounded
   * by planes, and it is capable of computing intersections of
   * boxes with itself.  Return 1 if this spatial region intersects
   * the spatial region described by the vtkPlanesIntersection object.
   * Use the possibly smaller bounds of the points within the region
   * if useDataBounds is non-zero.
   */
  int IntersectsRegion(vtkPlanesIntersection *pi, int useDataBounds);

  /**
   * Return 1 if this spatial region entirely contains the given point.
   * Use the possibly smaller bounds of the points within the region
   * if useDataBounds is non-zero.
   */
  vtkTypeBool ContainsPoint(double x, double y, double z, int useDataBounds);

  /**
   * Calculate the distance squared from any point to the boundary of this
   * region.  Use the boundary of the points within the region if useDataBounds
   * is non-zero.
   */
  double GetDistance2ToBoundary(double x, double y, double z,
                                vtkOctreePointLocatorNode* top, int useDataBounds);

  /**
   * Calculate the distance squared from any point to the boundary of this
   * region.  Use the boundary of the points within the region if useDataBounds
   * is non-zero.  Set boundaryPt to the point on the boundary.
   */
  double GetDistance2ToBoundary(double x, double y, double z,
                                double *boundaryPt, vtkOctreePointLocatorNode* top,
                                int useDataBounds);

  /**
   * Calculate the distance from the specified point (which is required to
   * be inside this spatial region) to an interior boundary.  An interior
   * boundary is one that is not also an boundary of the entire space
   * partitioned by the tree of vtkOctreePointLocatorNode's.
   */
  double GetDistance2ToInnerBoundary(double x, double y, double z,
                                     vtkOctreePointLocatorNode* top);

  /**
   * Return the id of the suboctant that a given point is in.
   * If CheckContainment is non-zero then it checks whether
   * the point is in the actual bounding box of the suboctant,
   * otherwise it only checks which octant the point is in
   * that is created from the axis-aligned partitioning of
   * the domain at this octant's center.
   */
  int GetSubOctantIndex(double* point, int CheckContainment);

  /**
   * Recursive function to compute ID, MinVal, MaxVal, and MinID.
   * Parent is used for MinVal and MaxVal in the case that no
   * points are in the leaf node.
   */
  void ComputeOctreeNodeInformation(vtkOctreePointLocatorNode* Parent,
                                    int& NextLeafId, int & NextMinId,
                                    float* coordinates);

protected:
  vtkOctreePointLocatorNode();
  ~vtkOctreePointLocatorNode() override;

private:

  double _GetDistance2ToBoundary(
    double x, double y, double z, double *boundaryPt,
    int innerBoundaryOnly, vtkOctreePointLocatorNode* top,
    int useDataBounds);

  /**
   * The minimum coordinate location of the node.
   */
  double MinBounds[3];

  /**
   * The maximum coordinate location of the node.
   */
  double MaxBounds[3];

  /**
   * The minimum coordinate location of the points contained
   * within this node.
   */
  double MinDataBounds[3];

  /**
   * The maximum coordinate location of the points contained
   * within this node.
   */
  double MaxDataBounds[3];

  /**
   * Get the number of points associated with this octant.
   * The octant does not have to be a leaf octant.  For example,
   * for the root octant NumberOfPoints is equal to the number
   * of points in the dataset.
   */
  int NumberOfPoints;

  /**
   * A pointer to the 8 children of this node.
   */
  vtkOctreePointLocatorNode** Children;

  /**
   * The ID of this octant.  If it is not a leaf octant then ID=-1.
   */
  int ID;

  /**
   * The minimum Id of the ordered points in this octant (note that
   * this Id is different than the vtkIdType used for referencing
   * the point in the data set.
   */
  int MinID;

  vtkOctreePointLocatorNode(const vtkOctreePointLocatorNode&) = delete;
  void operator=(const vtkOctreePointLocatorNode&) = delete;
};

#endif
