#ifndef vtkLabelHierarchyPrivate_h
#define vtkLabelHierarchyPrivate_h

#include <set>

#include "octree/octree"

//----------------------------------------------------------------------------
// vtkLabelHierarchy::Implementation

class vtkLabelHierarchy::Implementation
{
public:
  Implementation()
  {
    this->Hierarchy2 = nullptr;
    this->Hierarchy3 = nullptr;
    this->ActualDepth = 5;
    this->Z2 = 0.;
  }

  ~Implementation()
  {
    delete this->Hierarchy2;
    delete this->Hierarchy3;
  }

  bool ComparePriorities( vtkIdType a, vtkIdType b )
  {
    vtkDataArray* priorities = this->Husk->GetPriorities();
    return priorities ?
      priorities->GetTuple1( a ) > priorities->GetTuple1( b ) :
      a < b;
  }

  struct PriorityComparator
  {
    vtkLabelHierarchy* Hierarchy;

    PriorityComparator()
    {
      // See comment near declaration of Current for more info:
      this->Hierarchy = vtkLabelHierarchy::Implementation::Current;
    }

    PriorityComparator( vtkLabelHierarchy* h )
    {
      this->Hierarchy = h;
    }

    PriorityComparator( const PriorityComparator& src )
    {
      this->Hierarchy = src.Hierarchy;
    }

    PriorityComparator& operator=(const PriorityComparator& rhs)
    {
      if (this != &rhs)
      {
        this->Hierarchy = rhs.Hierarchy;
      }
      return *this;
    }

    ~PriorityComparator()
    {
    }

    bool operator () ( const vtkIdType& a, const vtkIdType& b ) const
    {
      if (nullptr == this->Hierarchy)
      {
        vtkGenericWarningMacro( "error: NULL this->Hierarchy in PriorityComparator" );
        return a < b;
      }

      if (nullptr == this->Hierarchy->GetImplementation())
      {
        vtkGenericWarningMacro( "error: NULL this->Hierarchy->GetImplementation() in PriorityComparator" );
        return a < b;
      }

      return this->Hierarchy->GetImplementation()->ComparePriorities( a, b );
    }
  };

  class LabelSet : public std::multiset<vtkIdType,PriorityComparator>
  {
  public:
    LabelSet( vtkLabelHierarchy* hierarchy )
      : std::multiset<vtkIdType,PriorityComparator>( PriorityComparator(hierarchy) )
    {
      this->TotalAnchors = 0;
      this->Size = 1.;
      for ( int i = 0; i < 3; ++ i )
      {
        this->Center[i] = 0.;
      }
    }

    LabelSet( const LabelSet& src )
      : std::multiset<vtkIdType,PriorityComparator>( src )
    {
      this->TotalAnchors = src.TotalAnchors;
      this->Size = src.Size;
      for ( int i = 0; i < 3; ++ i )
      {
        this->Center[i] = src.Center[i];
      }
    }

    LabelSet()
      : std::multiset<vtkIdType,PriorityComparator>()
    {
      this->TotalAnchors = 0;
      this->Size = 1.;
      for ( int i = 0; i < 3; ++ i )
      {
        this->Center[i] = 0.;
      }
    }

    LabelSet& operator = ( const LabelSet& rhs )
    {
      if ( this != &rhs )
      {
        std::multiset<vtkIdType,PriorityComparator>::operator = ( rhs );
        this->TotalAnchors = rhs.TotalAnchors;
        this->Size = rhs.Size;
        for ( int i = 0; i < 3; ++ i )
        {
          this->Center[i] = rhs.Center[i];
        }
      }
      return *this;
    }
    const double* GetCenter() const { return this->Center; }
    double GetSize() const { return this->Size; }
    void SetGeometry( const double center[3], double length );
    void SetChildGeometry( octree<LabelSet,2>::octree_node_pointer self );
    void SetChildGeometry( octree<LabelSet,3>::octree_node_pointer self );
    void AddChildren( octree<LabelSet,2>::octree_node_pointer self, LabelSet& emptyNode );
    void AddChildren( octree<LabelSet,3>::octree_node_pointer self, LabelSet& emptyNode );
    void Insert( vtkIdType anchor )
    {
      this->insert( anchor );
      ++ this->TotalAnchors;
    }
    void Increment() { ++ this->TotalAnchors; }
    vtkIdType GetLocalAnchorCount() const { return static_cast<vtkIdType>(this->size()); }
    vtkIdType GetTotalAnchorCount() const { return this->TotalAnchors; }

    vtkIdType TotalAnchors; // Count of all anchors stored in this node and its children.
    double Center[3]; // Geometric coordinates of this node's center.
    double Size; // Length of each edge of this node.
  };

  typedef octree<LabelSet,2> HierarchyType2;
  typedef octree<LabelSet,2>::cursor HierarchyCursor2;
  typedef octree<LabelSet,2>::iterator HierarchyIterator2;

  typedef octree<LabelSet> HierarchyType3;
  typedef octree<LabelSet>::cursor HierarchyCursor3;
  typedef octree<LabelSet>::iterator HierarchyIterator3;

  //typedef std::map<Coord,std::pair<int,std::set<vtkIdType> > >::iterator MapCoordIter;

  // Description:
  // Computes the depth of the generated hierarchy.
  //void ComputeActualDepth();

  // Description:
  // Routines called by ComputeHierarchy()
  void BinAnchorsToLevel( int level );
  void PromoteAnchors();
  void DemoteAnchors( int level );
  void RecursiveNodeDivide( HierarchyCursor2& cursor );
  void RecursiveNodeDivide( HierarchyCursor3& cursor );

  // Description:
  // Routines called by ComputeHierarchy()
  void PrepareSortedAnchors( LabelSet& anchors );
  void FillHierarchyRoot( LabelSet& anchors );
  void DropAnchor2( vtkIdType anchor );
  void DropAnchor3( vtkIdType anchor );
  void SmudgeAnchor2( HierarchyCursor2& cursor, vtkIdType anchor, double* x );
  void SmudgeAnchor3( HierarchyCursor3& cursor, vtkIdType anchor, double* x );

  double Z2; // common z-coordinate of all label anchors when quadtree (Hierarchy2) is used.
  HierarchyType2* Hierarchy2; // 2-D quadtree of label anchors (all input points have same z coord)
  HierarchyType3* Hierarchy3; // 3-D octree of label anchors (input point bounds have non-zero z range)
  vtkTimeStamp HierarchyTime;
  HierarchyType3::size_type ActualDepth;
  vtkLabelHierarchy* Husk;

  static vtkLabelHierarchy* Current;
};

inline void vtkLabelHierarchy::Implementation::LabelSet::SetGeometry( const double center[3], double length )
{
  for ( int i = 0; i < 3; ++ i )
  {
    this->Center[i] = center[i];
  }
  this->Size = length;
}

inline void vtkLabelHierarchy::Implementation::LabelSet::SetChildGeometry( octree<LabelSet,2>::octree_node_pointer self )
{
  double sz2 = this->Size / 2.;
  double x[3];
  for ( int i = 0; i < self->num_children(); ++ i )
  {
    for ( int j = 0; j < 2; ++ j )
    {
      x[j] = this->Center[j] + ( ( i & (1<<j) ) ? 0.5 : -0.5 ) * sz2 ;
    }
    x[2] = this->Center[2];
    (*self)[i].value().SetGeometry( x, sz2 );
  }
}

inline void vtkLabelHierarchy::Implementation::LabelSet::SetChildGeometry( octree<LabelSet,3>::octree_node_pointer self )
{
  double sz2 = this->Size / 2.;
  double x[3];
  for ( int i = 0; i < self->num_children(); ++ i )
  {
    for ( int j = 0; j < 3; ++ j )
    {
      x[j] = this->Center[j] + ( ( i & (1<<j) ) ? 0.5 : -0.5 ) * sz2 ;
    }
    (*self)[i].value().SetGeometry( x, sz2 );
  }
}

inline void vtkLabelHierarchy::Implementation::LabelSet::AddChildren( octree<LabelSet,2>::octree_node_pointer self, LabelSet& emptyNode )
{
  self->add_children( emptyNode );
  this->SetChildGeometry( self );
}

inline void vtkLabelHierarchy::Implementation::LabelSet::AddChildren( octree<LabelSet,3>::octree_node_pointer self, LabelSet& emptyNode )
{
  self->add_children( emptyNode );
  this->SetChildGeometry( self );
}

#endif // vtkLabelHierarchyPrivate_h
// VTK-HeaderTest-Exclude: vtkLabelHierarchyPrivate.h
