// Created on: 2002-07-30
// Created by: Michael SAZONOV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef NCollection_UBTree_HeaderFile
#define NCollection_UBTree_HeaderFile

#include <NCollection_BaseAllocator.hxx>
#include <NCollection_DefineAlloc.hxx>

/**
 * The algorithm of unbalanced binary tree of overlapped bounding boxes.
 *
 * Once the tree of boxes  of geometric objects is constructed, the algorithm
 * is capable of fast geometric selection of objects.  The tree can be easily
 * updated by adding to it a new object with bounding box.
 *
 * The time of adding to the tree  of one object is O(log(N)), where N is the
 * total number of  objects, so the time  of building a tree of  N objects is
 * O(N(log(N)). The search time of one object is O(log(N)).
 *
 * Defining  various classes  inheriting NCollection_UBTree::Selector  we can
 * perform various kinds of selection over the same b-tree object
 *
 * The object  may be of any  type allowing copying. Among  the best suitable
 * solutions there can  be a pointer to an object,  handled object or integer
 * index of object inside some  collection.  The bounding object may have any
 * dimension  and  geometry. The  minimal  interface  of TheBndType  (besides
 * public empty and copy constructor and operator =) used in UBTree algorithm
 * is as the following:
 * @code
 *   class MyBndType
 *   {
 *    public:
 *     inline void                   Add (const MyBndType& other);
 *     // Updates me with other bounding
 *
 *     inline bool       IsOut (const MyBndType& other) const;
 *     // Classifies other bounding relatively me
 *
 *     inline double          SquareExtent() const;
 *     // Computes the squared maximal linear extent of me.
 *     // (For box it is the squared diagonal of box)
 *   };
 * @endcode
 * To select objects you need to define a class derived from UBTree::Selector
 * that  should  redefine  the  necessary  virtual methods  to  maintain  the
 * selection condition.  The object  of this class  is also used  to retrieve
 * selected objects after search.
 */

template <class TheObjType, class TheBndType>
class NCollection_UBTree
{
public:
  //! Memory allocation
  DEFINE_STANDARD_ALLOC
  DEFINE_NCOLLECTION_ALLOC

public:
  // ---------- PUBLIC TYPES ----------

  /**
   * Class defining the minimal interface of selector.
   */
  class Selector
  {
  public:
    /**
     * Constructor
     */
    Selector()
        : myStop(false)
    {
    }

    /**
     * Rejection base on the bounding type.
     * @return
     *   True if the bounding box does not conform to some selection conditions
     */
    virtual bool Reject(const TheBndType&) const = 0;

    /**
     * Confirm the object while making necessary tests on it. This method is
     * called when the bounding box of the object conforms to the conditions
     * (see Reject()). It is also supposed to keep record of accepted objects.
     * @return
     *   True if the object is accepted
     */
    virtual bool Accept(const TheObjType&) = 0;

    /**
     * This condition is checked after each call to Accept().
     * @return
     *   True signals that the selection process is stopped
     */
    bool Stop() const noexcept { return myStop; }

    /**
     * Destructor
     */
    virtual ~Selector() = default;

  protected:
    /**
     * The method Accept() should set this flag if the selection process
     * is to be stopped
     */
    bool myStop;
  };

  /**
   * Class describing the node of the tree.
   * Initially the tree consists of one leaf. A node can grow to a branch
   * holding two childs:
   * - one correspondent to initial node
   * - the new one with a new object and bounding box
   */
  class TreeNode
  {
  public:
    DEFINE_STANDARD_ALLOC
    DEFINE_NCOLLECTION_ALLOC

  public:
    TreeNode(const TheObjType& theObj, const TheBndType& theBnd)
        : myBnd(theBnd),
          myObject(theObj),
          myChildren(nullptr),
          myParent(nullptr)
    {
    }

    bool IsLeaf() const noexcept { return !myChildren; }

    bool IsRoot() const noexcept { return !myParent; }

    const TheBndType& Bnd() const noexcept { return myBnd; }

    TheBndType& ChangeBnd() noexcept { return myBnd; }

    const TheObjType& Object() const noexcept { return myObject; }

    const TreeNode& Child(const int i) const noexcept { return myChildren[i]; }

    TreeNode& ChangeChild(const int i) noexcept { return myChildren[i]; }

    const TreeNode& Parent() const noexcept { return *myParent; }

    TreeNode& ChangeParent() noexcept { return *myParent; }

    /**
     * Forces *this node being gemmated such a way that it becomes
     * a branch holding the previous content of *this node at the
     * first child and theObj at the second child.
     * @param theNewBnd
     *   new bounding box comprizing both child nodes.
     * @param theObj
     *   added object.
     * @param theBnd
     *   bounding box of theObj.
     * @theAlloc
     *   allocator providing memory to the new child nodes, provided by the
     *   calling Tree instance.
     */
    void Gemmate(const TheBndType&                             theNewBnd,
                 const TheObjType&                             theObj,
                 const TheBndType&                             theBnd,
                 const occ::handle<NCollection_BaseAllocator>& theAlloc)
    {
      // TreeNode *children = new TreeNode [2];
      TreeNode* children = (TreeNode*)theAlloc->Allocate(2 * sizeof(TreeNode));
      new (&children[0]) TreeNode;
      new (&children[1]) TreeNode;
      children[0]          = *this;
      children[1].myObject = theObj;
      children[1].myBnd    = theBnd;
      children[0].myParent = children[1].myParent = this;
      if (!IsLeaf())
      {
        myChildren[0].myParent = children;
        myChildren[1].myParent = children;
      }
      myChildren = children;
      myBnd      = theNewBnd;
      myObject   = TheObjType(); // nullify myObject
    }

    /**
     * Kills the i-th child, and *this accepts the content of another child
     */
    void Kill(const int i, const occ::handle<NCollection_BaseAllocator>& theAlloc)
    {
      if (!IsLeaf())
      {
        TreeNode* oldChildren = myChildren;
        const int iopp        = 1 - i;
        myBnd                 = oldChildren[iopp].myBnd;
        myObject              = oldChildren[iopp].myObject;
        myChildren            = oldChildren[iopp].myChildren;
        if (!IsLeaf())
        {
          myChildren[0].myParent = this;
          myChildren[1].myParent = this;
        }
        // oldChildren[0].myChildren = oldChildren[1].myChildren = 0L;
        // delete [] oldChildren;
        oldChildren[iopp].~TreeNode();
        delNode(&oldChildren[i], theAlloc); // remove the whole branch
        theAlloc->Free(oldChildren);
      }
    }

    //  ~TreeNode () { if (myChildren) delete [] myChildren; }
    ~TreeNode() { myChildren = nullptr; }

    /**
     * Deleter of tree node. The whole hierarchy of its children also deleted.
     * This method should be used instead of operator delete.
     */
    static void delNode(TreeNode* theNode, const occ::handle<NCollection_BaseAllocator>& theAlloc)
    {
      if (theNode)
      {
        if (theNode->myChildren)
        {
          delNode(&theNode->myChildren[0], theAlloc);
          delNode(&theNode->myChildren[1], theAlloc);
          theAlloc->Free(theNode->myChildren);
        }
        theNode->~TreeNode();
      }
    }

  private:
    TreeNode()
        : myChildren(nullptr),
          myParent(nullptr)
    {
    }

    TheBndType myBnd;      ///< bounding geometry
    TheObjType myObject;   ///< the object
    TreeNode*  myChildren; ///< 2 children forming a b-tree
    TreeNode*  myParent;   ///< the pointer to a parent node
  };

  // ---------- PUBLIC METHODS ----------

  /**
   * Empty constructor.
   */
  NCollection_UBTree()
      : myRoot(nullptr),
        myLastNode(nullptr),
        myAlloc(NCollection_BaseAllocator::CommonBaseAllocator())
  {
  }

  /**
   * Constructor.
   */
  explicit NCollection_UBTree(const occ::handle<NCollection_BaseAllocator>& theAllocator)
      : myRoot(nullptr),
        myLastNode(nullptr),
        myAlloc(!theAllocator.IsNull() ? theAllocator
                                       : NCollection_BaseAllocator::CommonBaseAllocator())
  {
  }

  /**
   * Update the tree with a new object and its bounding box.
   * @param theObj
   *   added object
   * @param theBnd
   *   bounding box of the object.
   * @return
   *   always True
   */
  virtual bool Add(const TheObjType& theObj, const TheBndType& theBnd);

  /**
   * Searches in the tree all objects conforming to the given selector.
   * return
   *   Number of objects accepted
   */
  virtual int Select(Selector& theSelector) const
  {
    return (IsEmpty() ? 0 : Select(Root(), theSelector));
  }

  /**
   * Clears the contents of the tree.
   * @param aNewAlloc
   *   Optional:   a new allocator that will be used when the tree is rebuilt
   *   anew. This makes sense if the memory allocator needs re-initialisation
   *   (like NCollection_IncAllocator).  By default the previous allocator is
   *   kept.
   */
  virtual void Clear(const occ::handle<NCollection_BaseAllocator>& aNewAlloc = nullptr)
  //      { if (myRoot) delete myRoot; myRoot = 0L; }
  {
    if (myRoot)
    {
      TreeNode::delNode(myRoot, this->myAlloc);
      this->myAlloc->Free(myRoot);
      myRoot = nullptr;
    }
    if (aNewAlloc.IsNull() == false)
      myAlloc = aNewAlloc;
  }

  bool IsEmpty() const noexcept { return !myRoot; }

  /**
   * @return
   *   the root node of the tree
   */
  const TreeNode& Root() const noexcept { return *myRoot; }

  /**
   * Destructor.
   */
  virtual ~NCollection_UBTree() { Clear(); }

  /**
   * Recommended to be used only in sub-classes.
   * @return
   *   Allocator object used in this instance of UBTree.
   */
  const occ::handle<NCollection_BaseAllocator>& Allocator() const noexcept { return myAlloc; }

protected:
  // ---------- PROTECTED METHODS ----------

  /**
   * @return
   *   the last added node
   */
  TreeNode& ChangeLastNode() noexcept { return *myLastNode; }

  /**
   * Searches in the branch all objects conforming to the given selector.
   * @return
   *   the number of objects accepted
   */
  int Select(const TreeNode& theBranch, Selector& theSelector) const;

private:
  // ---------- PRIVATE METHODS ----------

  /// Copy constructor (prohibited).
  NCollection_UBTree(const NCollection_UBTree&) = delete;

  /// Assignment operator (prohibited).
  NCollection_UBTree& operator=(const NCollection_UBTree&) = delete;

  // ---------- PRIVATE FIELDS ----------

  TreeNode*                              myRoot;     ///< root of the tree
  TreeNode*                              myLastNode; ///< the last added node
  occ::handle<NCollection_BaseAllocator> myAlloc;    ///< Allocator for TreeNode
};

// ================== METHODS TEMPLATES =====================
//=======================================================================
// function : Add
// purpose  : Updates the tree with a new object and its bounding box
//=======================================================================

template <class TheObjType, class TheBndType>
bool NCollection_UBTree<TheObjType, TheBndType>::Add(const TheObjType& theObj,
                                                     const TheBndType& theBnd)
{
  if (IsEmpty())
  {
    // Accepting first object
    myRoot     = new (this->myAlloc) TreeNode(theObj, theBnd);
    myLastNode = myRoot;
    return true;
  }

  TreeNode* pBranch       = myRoot;
  bool      isOutOfBranch = pBranch->Bnd().IsOut(theBnd);

  for (;;)
  {
    // condition of stopping the search
    if (isOutOfBranch || pBranch->IsLeaf())
    {
      TheBndType aNewBnd = theBnd;
      aNewBnd.Add(pBranch->Bnd());
      // put the new leaf aside on the level of pBranch
      pBranch->Gemmate(aNewBnd, theObj, theBnd, this->myAlloc);
      myLastNode = &pBranch->ChangeChild(1);
      break;
    }

    // Update the bounding box of the branch
    pBranch->ChangeBnd().Add(theBnd);

    // Select the best child branch to accept the object:
    // 1. First check if one branch is out and another one is not.
    // 2. Else select the child having the least union with theBnd
    int  iBest   = 0;
    bool isOut[] = {pBranch->Child(0).Bnd().IsOut(theBnd), pBranch->Child(1).Bnd().IsOut(theBnd)};
    if (isOut[0] != isOut[1])
      iBest = (isOut[0] ? 1 : 0);
    else
    {
      TheBndType aUnion[] = {theBnd, theBnd};
      aUnion[0].Add(pBranch->Child(0).Bnd());
      aUnion[1].Add(pBranch->Child(1).Bnd());
      const double d1 = aUnion[0].SquareExtent();
      const double d2 = aUnion[1].SquareExtent();
      if (d1 > d2)
        iBest = 1;
    }

    // Continue with the selected branch
    isOutOfBranch = isOut[iBest];
    pBranch       = &pBranch->ChangeChild(iBest);
  }
  return true;
}

//=======================================================================
// function : Select
// purpose  : Recursively searches in the branch all objects conforming
//           to the given selector.
//           Returns the number of objects found.
//=======================================================================

template <class TheObjType, class TheBndType>
int NCollection_UBTree<TheObjType, TheBndType>::Select(const TreeNode& theBranch,
                                                       Selector&       theSelector) const
{
  // Try to reject the branch by bounding box
  if (theSelector.Reject(theBranch.Bnd()))
    return 0;

  int nSel = 0;

  if (theBranch.IsLeaf())
  {
    // It is a leaf => try to accept the object
    if (theSelector.Accept(theBranch.Object()))
      nSel++;
  }
  else
  {
    // It is a branch => select from its children
    nSel += Select(theBranch.Child(0), theSelector);
    if (!theSelector.Stop())
      nSel += Select(theBranch.Child(1), theSelector);
  }

  return nSel;
}

// ======================================================================
/**
 * Declaration of handled version of NCollection_UBTree.
 * In the macros below the arguments are:
 * _HUBTREE      - the desired name of handled class
 * _OBJTYPE      - the name of the object type
 * _BNDTYPE      - the name of the bounding box type
 * _HPARENT      - the name of parent class (usually Standard_Transient)
 */
#define DEFINE_HUBTREE(_HUBTREE, _OBJTYPE, _BNDTYPE, _HPARENT)                                     \
  class _HUBTREE : public _HPARENT                                                                 \
  {                                                                                                \
  public:                                                                                          \
    typedef NCollection_UBTree<_OBJTYPE, _BNDTYPE> UBTree;                                         \
                                                                                                   \
    _HUBTREE()                                                                                     \
        : myTree(new UBTree)                                                                       \
    {                                                                                              \
    }                                                                                              \
    /* Empty constructor */                                                                        \
    _HUBTREE(const occ::handle<NCollection_BaseAllocator>& theAlloc)                               \
        : myTree(new UBTree(theAlloc))                                                             \
    {                                                                                              \
    }                                                                                              \
    /* Constructor */                                                                              \
                                                                                                   \
    /* Access to the methods of UBTree */                                                          \
                                                                                                   \
    bool Add(const _OBJTYPE& theObj, const _BNDTYPE& theBnd)                                       \
    {                                                                                              \
      return ChangeTree().Add(theObj, theBnd);                                                     \
    }                                                                                              \
                                                                                                   \
    int Select(UBTree::Selector& theSelector) const                                                \
    {                                                                                              \
      return Tree().Select(theSelector);                                                           \
    }                                                                                              \
                                                                                                   \
    void Clear()                                                                                   \
    {                                                                                              \
      ChangeTree().Clear();                                                                        \
    }                                                                                              \
                                                                                                   \
    bool IsEmpty() const noexcept                                                                  \
    {                                                                                              \
      return Tree().IsEmpty();                                                                     \
    }                                                                                              \
                                                                                                   \
    const UBTree::TreeNode& Root() const                                                           \
    {                                                                                              \
      return Tree().Root();                                                                        \
    }                                                                                              \
                                                                                                   \
    /* Access to the tree algorithm */                                                             \
                                                                                                   \
    const UBTree& Tree() const noexcept                                                            \
    {                                                                                              \
      return *myTree;                                                                              \
    }                                                                                              \
    UBTree& ChangeTree() noexcept                                                                  \
    {                                                                                              \
      return *myTree;                                                                              \
    }                                                                                              \
                                                                                                   \
    ~_HUBTREE()                                                                                    \
    {                                                                                              \
      delete myTree;                                                                               \
    }                                                                                              \
    /* Destructor */                                                                               \
                                                                                                   \
    DEFINE_STANDARD_RTTI_INLINE(_HUBTREE, _HPARENT)                                                \
    /* Type management */                                                                          \
                                                                                                   \
  private:                                                                                         \
    /* Copying and assignment are prohibited  */                                                   \
    _HUBTREE(UBTree*);                                                                             \
    _HUBTREE(const _HUBTREE&);                                                                     \
    void operator=(const _HUBTREE&);                                                               \
                                                                                                   \
  private:                                                                                         \
    UBTree* myTree; /* pointer to the tree algorithm */                                            \
  };                                                                                               \
  DEFINE_STANDARD_HANDLE(_HUBTREE, _HPARENT)

#define IMPLEMENT_HUBTREE(_HUBTREE, _HPARENT)

#endif
