/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#ifndef POINTS_GRID_H
#define POINTS_GRID_H

#include <set>

#include "Points.h"
#include <Base/Vector3D.h>
#include <Base/BoundBox.h>

#define  POINTS_CT_GRID          256     // Default value for number of elements per grid
#define  POINTS_MAX_GRIDS        100000  // Default value for maximum number of grids
#define  POINTS_CT_GRID_PER_AXIS 20
#define  PONTSGRID_BBOX_EXTENSION 10.0f


namespace Points {
class PointsGrid;

/**
 * The PointsGrid allows to divide a global point cloud into smaller regions of elements depending on the resolution of the grid. 
 * All grid elements in the grid structure have the same size.
 *
 * Grids can be used within algorithms to avoid to iterate through all elements, so grids can speed up algorithms dramatically.
 * @author Werner Mayer
 */
class PointsExport PointsGrid
{
public:
  /** @name Construction */
  //@{
  /// Construction
  PointsGrid (const PointKernel &rclM);
  /// Construction
  PointsGrid (void);
  /// Construction
  PointsGrid (const PointKernel &rclM, int   iCtGridPerAxis);
  /// Construction
  PointsGrid (const PointKernel &rclM, double fGridLen);
  /// Construction
  PointsGrid (const PointKernel &rclM, unsigned long ulX, unsigned long ulY, unsigned long ulZ);
  /// Destruction
  virtual ~PointsGrid (void) { }
  //@}

public:
  /** Attaches the point kernel to this grid, an already attached point cloud gets detached. The grid gets rebuilt 
   * automatically. */
  virtual void Attach (const PointKernel &rclM);
  /** Rebuilds the grid structure. */
  virtual void Rebuild (unsigned long ulPerGrid = POINTS_CT_GRID, unsigned long ulMaxGrid = POINTS_MAX_GRIDS);
  /** Rebuilds the grid structure. */
  virtual void Rebuild (int iCtGridPerAxis = POINTS_CT_GRID_PER_AXIS);
  /** Rebuilds the grid structure. */
  virtual void Rebuild (unsigned long ulX, unsigned long ulY, unsigned long ulZ);

  /** @name Search */
  //@{
  /** Searches for elements lying in the intersection area of the grid and the bounding box. */
  virtual unsigned long InSide (const Base::BoundBox3d &rclBB, std::vector<unsigned long> &raulElements, bool bDelDoubles = true) const;
  /** Searches for elements lying in the intersection area of the grid and the bounding box. */
  virtual unsigned long InSide (const Base::BoundBox3d &rclBB, std::set<unsigned long> &raulElementss) const;
  /** Searches for elements lying in the intersection area of the grid and the bounding box. */
  virtual unsigned long InSide (const Base::BoundBox3d &rclBB, std::vector<unsigned long> &raulElements,
                                const Base::Vector3d &rclOrg, double fMaxDist, bool bDelDoubles = true) const;
  /** Searches for the nearest grids that contain elements from a point, the result are grid indices. */
  void SearchNearestFromPoint (const Base::Vector3d &rclPt, std::set<unsigned long> &rclInd) const;
  //@}

  /** Returns the lengths of the grid elements in x,y and z direction. */
  virtual void  GetGridLengths (double &rfLenX, double &rfLenY, double &rfLenZ) const
  { rfLenX = _fGridLenX; rfLenY = _fGridLenY; rfLenZ = _fGridLenZ; }
  /** Returns the number of grid elements in x,y and z direction. */
  virtual void GetCtGrids (unsigned long &rulX, unsigned long &rulY, unsigned long &rulZ) const
  { rulX = _ulCtGridsX;  rulY = _ulCtGridsY;  rulZ = _ulCtGridsZ; }

  /** @name Boundings */
  //@{
  /** Returns the bounding box of a given grid element. */
  inline Base::BoundBox3d  GetBoundBox (unsigned long ulX, unsigned long ulY, unsigned long ulZ) const;
  /** Returns the bounding box of the whole. */
  inline Base::BoundBox3d  GetBoundBox     (void) const;
  //@}
  /** Returns the number of elements in a given grid. */
  unsigned long GetCtElements(unsigned long ulX, unsigned long ulY, unsigned long ulZ) const
  { return _aulGrid[ulX][ulY][ulZ].size(); }
  /** Finds all points that lie in the same grid as the point \a rclPoint. */
  unsigned long FindElements(const Base::Vector3d &rclPoint, std::set<unsigned long>& aulElements) const;
  /** Validates the grid structure and rebuilds it if needed. */
  virtual void Validate (const PointKernel &rclM);
  /** Validates the grid structure and rebuilds it if needed. */
  virtual void Validate (void);
  /** Verifies the grid structure and returns false if inconsistencies are found. */
  virtual bool Verify() const;
  /** Returns the indices of the grid this point lies in. If the point is outside the grid then the indices of 
   * the nearest grid element are taken.*/
  virtual void Position (const Base::Vector3d &rclPoint, unsigned long &rulX, unsigned long &rulY, unsigned long &rulZ) const;
  /** Returns the indices of the elements in the given grid. */
  unsigned long GetElements (unsigned long ulX, unsigned long ulY, unsigned long ulZ,  std::set<unsigned long> &raclInd) const;

protected:
  /** Checks if this is a valid grid position. */
  inline bool CheckPos (unsigned long ulX, unsigned long ulY, unsigned long ulZ) const;
  /** Initializes the size of the internal structure. */
  virtual void InitGrid (void);
  /** Deletes the grid structure. */
  virtual void Clear (void);
  /** Calculates the grid length dependent on maximum number of grids. */
  virtual void CalculateGridLength (unsigned long ulCtGrid, unsigned long ulMaxGrids);
  /** Calculates the grid length dependent on the number of grids per axis. */
  virtual void CalculateGridLength (int    iCtGridPerAxis);
  /** Rebuilds the grid structure. */
  virtual void RebuildGrid (void);
  /** Returns the number of stored elements. */
  unsigned long HasElements (void) const
  { return _pclPoints->size(); }
  /** Get the indices of all elements lying in the grids around a given grid with distance \a ulDistance. */
  void GetHull (unsigned long ulX, unsigned long ulY, unsigned long ulZ, unsigned long ulDistance, std::set<unsigned long> &raclInd) const;

protected:
  std::vector<std::vector<std::vector<std::set<unsigned long> > > >  _aulGrid;   /**< Grid data structure. */
  const PointKernel* _pclPoints;  /**< The point kernel. */
  unsigned long     _ulCtElements;/**< Number of grid elements for validation issues. */
  unsigned long     _ulCtGridsX;  /**< Number of grid elements in z. */
  unsigned long     _ulCtGridsY;  /**< Number of grid elements in z. */
  unsigned long     _ulCtGridsZ;  /**< Number of grid elements in z. */
  double             _fGridLenX;   /**< Length of grid elements in x. */
  double             _fGridLenY;   /**< Length of grid elements in y. */
  double             _fGridLenZ;   /**< Length of grid elements in z. */
  double             _fMinX;       /**< Grid null position in x. */
  double             _fMinY;       /**< Grid null position in y. */ 
  double             _fMinZ;       /**< Grid null position in z. */

  // friends
  friend class PointsGridIterator;
  friend class PointsGridIteratorStatistic;

public:

protected:
  /** Adds a new point element to the grid structure. \a rclPt is the geometric point and \a ulPtIndex 
   * the corresponding index in the point kernel. */
  void AddPoint (const Base::Vector3d &rclPt, unsigned long ulPtIndex, float fEpsilon = 0.0f);
  /** Returns the grid numbers to the given point \a rclPoint. */
  void Pos(const Base::Vector3d &rclPoint, unsigned long &rulX, unsigned long &rulY, unsigned long &rulZ) const;
};

/**
 * The PointsGridIterator class provides an interface to walk through
 * all grid elements of a point grid.
 */
class PointsExport PointsGridIterator
{
public:
  /// Construction
  PointsGridIterator (const PointsGrid &rclG);
  /** Returns the bounding box of the current grid element. */
  Base::BoundBox3d GetBoundBox (void) const
  { return _rclGrid.GetBoundBox(_ulX, _ulY, _ulZ); }
  /** Returns indices of the elements in the current grid. */
  void GetElements (std::vector<unsigned long> &raulElements) const
  {
    raulElements.insert(raulElements.end(), _rclGrid._aulGrid[_ulX][_ulY][_ulZ].begin(), _rclGrid._aulGrid[_ulX][_ulY][_ulZ].end());
  }
  /** @name Iteration */
  //@{
  /** Sets the iterator to the first element*/
  void  Init (void)
  { _ulX = _ulY = _ulZ = 0; }
  /** Checks if the iterator has not yet reached the end position. */  
  bool  More (void) const
  { return (_ulZ < _rclGrid._ulCtGridsZ); }
  /** Go to the next grid. */
  void  Next (void)
  {
    if (++_ulX >= (_rclGrid._ulCtGridsX)) _ulX = 0; else return;
    if (++_ulY >= (_rclGrid._ulCtGridsY)) { _ulY = 0; _ulZ++; } else return;
  }
  //@}

  /** @name Tests with rays */
  //@{
  /** Searches for facets around the ray. */
  bool InitOnRay (const Base::Vector3d &rclPt, const Base::Vector3d &rclDir, std::vector<unsigned long> &raulElements);
  /** Searches for facets around the ray. */
  bool InitOnRay (const Base::Vector3d &rclPt, const Base::Vector3d &rclDir, float fMaxSearchArea, std::vector<unsigned long> &raulElements);
  /** Searches for facets around the ray. */
  bool NextOnRay (std::vector<unsigned long> &raulElements);
  //@}
  
  /** Returns the grid number of the current position. */
  void GetGridPos (unsigned long &rulX, unsigned long &rulY, unsigned long &rulZ) const
  { rulX = _ulX; rulY = _ulY; rulZ = _ulZ; }

protected:
  const PointsGrid& _rclGrid; /**< The point grid. */
  unsigned long   _ulX;     /**< Number of grids in x. */
  unsigned long   _ulY;     /**< Number of grids in y. */
  unsigned long   _ulZ;     /**< Number of grids in z. */ 
  Base::Vector3d  _clPt;    /**< Base point of search ray. */
  Base::Vector3d  _clDir;   /**< Direction of search ray. */
  bool            _bValidRay; /**< Search ray ok? */
  float           _fMaxSearchArea;
  /** Checks if a grid position is already visited by NextOnRay(). */
  struct GridElement 
  {
    GridElement( unsigned long x, unsigned long y, unsigned long z)
    { this->x = x; this->y = y; this->z = z; }
    bool operator < (const GridElement& pos) const
    {
      if ( x == pos.x)
      { if ( y == pos.y) return z < pos.z; else return y < pos.y; }
      else 
      { return x < pos.x; }
    }
  private:
    unsigned long x,y,z;
  };
  std::set<GridElement> _cSearchPositions;
};

// --------------------------------------------------------------

inline Base::BoundBox3d  PointsGrid::GetBoundBox (unsigned long ulX, unsigned long ulY, unsigned long ulZ) const
{
  double fX, fY, fZ;
  
  fX = _fMinX + (double(ulX) * _fGridLenX);
  fY = _fMinY + (double(ulY) * _fGridLenY);
  fZ = _fMinZ + (double(ulZ) * _fGridLenZ);

  return Base::BoundBox3d(fX, fY, fZ, fX + _fGridLenX, fY + _fGridLenY, fZ + _fGridLenZ);
}

inline Base::BoundBox3d  PointsGrid::GetBoundBox (void) const
{
  return Base::BoundBox3d(_fMinX, _fMinY, _fMinZ, _fMinX + (_fGridLenX * double(_ulCtGridsX)),
                          _fMinY + (_fGridLenY * double(_ulCtGridsY)), _fMinZ + (_fGridLenZ * double(_ulCtGridsZ)));
}

inline bool PointsGrid::CheckPos (unsigned long ulX, unsigned long ulY, unsigned long ulZ) const
{
  return ((ulX < _ulCtGridsX) && (ulY < _ulCtGridsY) && (ulZ < _ulCtGridsZ));
}

// --------------------------------------------------------------

} // namespace Points

#endif // POINTS_GRID_H
