// Copyright (c) 2005,2006,2007,2008,2009,2010,2011 Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// 
//
// Author(s)     : Ron Wein <wein@post.tau.ac.il>
//                 (based on old version by: Iddo Hanniel and Oren Nechushtan)

#ifndef CGAL_ARR_DCEL_BASE_H
#define CGAL_ARR_DCEL_BASE_H

/*! \file
 * The definition of the base DCEL class for planar arrangements and its
 * peripheral records.
 */

#include <CGAL/basic.h>
#include <CGAL/Arr_enums.h>
#include <list>
#include <map>
#include <CGAL/N_step_adaptor_derived.h>
#include <CGAL/In_place_list.h>
#include <CGAL/function_objects.h>
#include <CGAL/Iterator_project.h>
#include <CGAL/Arrangement_2/Arrangement_2_iterators.h>
#include <CGAL/assertions.h>


namespace CGAL {

inline void* _clean_pointer (const void* p)
{
  CGAL_static_assertion(sizeof(void*) == sizeof(size_t));
  const size_t  mask = ~1;
  const size_t  val = (reinterpret_cast<size_t>(p) & mask);

  return (reinterpret_cast<void*> (val));
}

inline void* _set_lsb (const void* p)
{
  const size_t  mask = 1;
  const size_t  val = (reinterpret_cast<size_t>(p) | mask);

  return (reinterpret_cast<void*> (val));
}

inline bool _is_lsb_set (const void* p)
{
  const size_t  mask = 1;
  const size_t  val = reinterpret_cast<size_t>(p);

  return ((val & mask) != 0); 
}

/*! \class
 * Base vertex class. 
 */
template <class Point_> class Arr_vertex_base 
{
public:

  typedef Point_       Point;

  /*! \struct
   * An auxiliary structure for rebinding the vertex with a new point class.
   */
  template<typename PNT>
  struct rebind
  {
    typedef Arr_vertex_base<PNT>             other;
  };

protected:

  void       *p_inc;  // An incident halfedge pointing at the vertex,
                      // or the isolated vertex information (in case it is
                      // isolated). The LSB of the pointer indicates whether
                      // the vertex is isolated.
  Point      *p_pt;   // The point associated with the vertex.
  char        pss[2]; // The x and y parameter spaces (condensed in two bytes).

public:

  /*! Default constructor. */
  Arr_vertex_base() :
    p_inc (NULL),
    p_pt (NULL)
  {
    pss[0] = pss[1] = static_cast<char> (CGAL::ARR_INTERIOR);
  }
  
  /*! Destructor. */
  virtual ~Arr_vertex_base() {}

  /*! Check if the point pointer is NULL. */
  bool has_null_point () const
  {
    return (p_pt == NULL);
  }

  /*! Get the point (const version). */
  const Point& point() const 
  {
    CGAL_assertion (p_pt != NULL);
    return (*p_pt);
  }

  /*! Get the point (non-const version). */
  Point& point() 
  { 
    CGAL_assertion (p_pt != NULL);
    return (*p_pt);
  }

  /*! Set the point (may be a NULL point). */
  void set_point (Point *p) 
  {
    p_pt = p;
  }

  /*! Get the boundary type in x. */
  Arr_parameter_space parameter_space_in_x () const
  {
    return (Arr_parameter_space (pss[0]));
  }

  /*! Get the boundary type in y. */
  Arr_parameter_space parameter_space_in_y () const
  {
    return (Arr_parameter_space (pss[1]));
  }

  /*! Set the boundary conditions of the vertex. */
  void set_boundary (Arr_parameter_space ps_x, Arr_parameter_space ps_y)
  {
    pss[0] = static_cast<char> (ps_x);
    pss[1] = static_cast<char> (ps_y);
    return;
  }

  /*! Assign from another vertex. */
  virtual void assign (const Arr_vertex_base<Point>& v)
  {
    p_pt = v.p_pt;
    pss[0] = v.pss[0];
    pss[1] = v.pss[1];
  }
};

/*! \class
 * Base halfedge class.
 */
template <class X_monotone_curve_> class Arr_halfedge_base 
{
public:

  typedef X_monotone_curve_  X_monotone_curve;

  /*! \struct
   * An auxiliary structure for rebinding the halfedge with a new curve class.
   */
  template<typename XCV>
  struct rebind
  {
    typedef Arr_halfedge_base<XCV>           other;
  };

protected:

  void       *p_opp;   // The opposite halfedge.
  void       *p_prev;  // The previous halfedge in the component boundary.
  void       *p_next;  // The next halfedge in the component boundary.

  void       *p_v;     // The incident vertex (the target of the halfedge).
                       // The LSB of this pointer is used to store the
                       // direction of the halfedge.
  void       *p_comp;  // The component this halfedge belongs to: the incident
                       // face for outer CCBs and the inner CCB information for
                       // inner CCBs. The LSB of the pointer indicates whether
                       // the halfedge lies on the boundary of an inner CCB.
  
  X_monotone_curve *p_cv; // The associated x-monotone curve.

public:

  /*! Default constructor */
  Arr_halfedge_base() :
    p_opp (NULL),
    p_prev (NULL),
    p_next (NULL),
    p_v (NULL),
    p_comp (NULL),
    p_cv (NULL)
  {}

  /*! Destructor. */
  virtual ~Arr_halfedge_base()
  {}

  /*! Check if the curve pointer is NULL. */
  bool has_null_curve () const
  {
    return (p_cv == NULL);
  }

  /*! Get the x-monotone curve (const version). */
  const X_monotone_curve& curve() const 
  {
    CGAL_precondition (p_cv != NULL);
    return (*p_cv);
  }

  /*! Get the x-monotone curve (non-const version). */
  X_monotone_curve& curve () 
  {
    CGAL_precondition (p_cv != NULL);
    return (*p_cv);
  }

  /*! Set the x-monotone curve. */
  void set_curve (X_monotone_curve* c)
  { 
    p_cv = c;

    // Set the curve for the opposite halfedge as well.
    Arr_halfedge_base<X_monotone_curve>* opp =
      reinterpret_cast<Arr_halfedge_base<X_monotone_curve>* > (p_opp);

    opp->p_cv = c;
  }

  /*! Assign from another halfedge. */
  virtual void assign (const Arr_halfedge_base<X_monotone_curve>& he)
  {
    p_cv = he.p_cv;
  }
};

/*!
 * Base face class.
 */
class Arr_face_base
{
public:

  typedef std::list<void*>                      Outer_ccbs_container;
  typedef Outer_ccbs_container::iterator        Outer_ccb_iterator;
  typedef Outer_ccbs_container::const_iterator  Outer_ccb_const_iterator;

  typedef std::list<void*>                      Inner_ccbs_container;
  typedef Inner_ccbs_container::iterator        Inner_ccb_iterator;
  typedef Inner_ccbs_container::const_iterator  Inner_ccb_const_iterator;

  typedef std::list<void*>                      Isolated_vertices_container;
  typedef Isolated_vertices_container::iterator Isolated_vertex_iterator;
  typedef Isolated_vertices_container::const_iterator
                                                Isolated_vertex_const_iterator;
  
protected:

  enum
  {
    IS_UNBOUNDED = 1,
    IS_FICTITIOUS = 2
  };

  int                          flags;      // Face flags.
  Outer_ccbs_container         outer_ccbs; // The outer CCBs of the faces.
  Inner_ccbs_container         inner_ccbs; // The inner CCBs of the face.
  Isolated_vertices_container  iso_verts;  // The isolated vertices inside
                                           // the face.

public:

  /*! Default constructor. */
  Arr_face_base() :
    flags (0)
  {}

  /*! Destructor. */
  virtual ~Arr_face_base()
  {}

  /*! Check if the face is unbounded. */
  bool is_unbounded () const
  {
    return ((flags & IS_UNBOUNDED) != 0);
  }

  /*! Set the face as bounded or unbounded. */
  void set_unbounded (bool unbounded)
  {
    flags = (unbounded) ? (flags | IS_UNBOUNDED) : (flags & ~IS_UNBOUNDED);
  }

  /*! Check if the face is fictitious. */
  bool is_fictitious () const
  {
    return ((flags & IS_FICTITIOUS) != 0);
  }

  /*! Set the face as fictitious or valid. */
  void set_fictitious (bool fictitious)
  {
    flags = (fictitious) ? (flags | IS_FICTITIOUS) : (flags & ~IS_FICTITIOUS);
  }

  /*! Assign from another face. */
  virtual void assign (const Arr_face_base& f)
  {
    flags = f.flags;
  }
};

// Forward declarations:
template <class V, class H, class F> class Arr_vertex;
template <class V, class H, class F> class Arr_halfedge;
template <class V, class H, class F> class Arr_face;
template <class V, class H, class F> class Arr_outer_ccb;
template <class V, class H, class F> class Arr_inner_ccb;
template <class V, class H, class F> class Arr_isolated_vertex;

/*! \class
 * The default arrangement DCEL vertex class.
 */
template <class V, class H, class F>
class Arr_vertex : public V,
                   public In_place_list_base<Arr_vertex<V,H,F> >
{
public:

  typedef V                           Base;
  typedef Arr_vertex<V,H,F>           Vertex;
  typedef Arr_halfedge<V,H,F>         Halfedge;
  typedef Arr_isolated_vertex<V,H,F>  Isolated_vertex;

  /*! Default constructor. */
  Arr_vertex() 
  {}

  /*! Check if the vertex is isolated. */
  bool is_isolated () const
  {
    // Note that we use the LSB of the p_inc pointer as a Boolean flag.
    return (_is_lsb_set (this->p_inc));
  }

  /*! Get an incident halfedge (const version). */
  const Halfedge* halfedge () const
  {
    CGAL_precondition (! is_isolated());
    return (reinterpret_cast<const Halfedge*>(this->p_inc));
  }

  /*! Get an incident halfedge (non-const version). */
  Halfedge* halfedge ()
  {
    CGAL_precondition (! is_isolated());
    return (reinterpret_cast<Halfedge*>(this->p_inc));
  }

  /*! Set an incident halfedge (for non-isolated vertices). */
  void set_halfedge (Halfedge* he)
  { 
    // Set the halfedge pointer and reset the LSB.
    this->p_inc = he;
  }

  /*! Get the isolated vertex information (const version). */
  const Isolated_vertex* isolated_vertex () const
  {
    CGAL_precondition (is_isolated());
    return (reinterpret_cast<const Isolated_vertex*>(_clean_pointer 
                                                     (this->p_inc)));
  }

  /*! Get the isolated vertex information (non-const version). */
  Isolated_vertex* isolated_vertex ()
  {
    CGAL_precondition (is_isolated());
    return (reinterpret_cast<Isolated_vertex*>(_clean_pointer (this->p_inc)));
  }

  /*! Set the isolated vertex information. */
  void set_isolated_vertex (Isolated_vertex* iv)
  {
    // Set the isolated vertex-information pointer and set its LSB.
    this->p_inc = _set_lsb (iv);
  }
};

/*! \class
 * The default arrangement DCEL halfedge class.
 */
template <class V, class H, class F>
class Arr_halfedge : public H,
                     public In_place_list_base<Arr_halfedge<V,H,F> >
{
public:

  typedef H                     Base;
  typedef Arr_vertex<V,H,F>     Vertex;
  typedef Arr_halfedge<V,H,F>   Halfedge;
  typedef Arr_face<V,H,F>       Face;
  typedef Arr_outer_ccb<V,H,F>  Outer_ccb;
  typedef Arr_inner_ccb<V,H,F>  Inner_ccb;

  /*! Default constructor. */
  Arr_halfedge()
  {}

  /*! Get the opposite halfedge (const version). */
  const Halfedge* opposite () const
  { 
    return (reinterpret_cast<const Halfedge*>(this->p_opp));
  }
  
  /*! Get the opposite halfedge (non-const version). */
  Halfedge* opposite ()
  { 
    return (reinterpret_cast<Halfedge*>(this->p_opp));
  }

  /*! Sets the opposite halfedge. */
  void set_opposite (Halfedge* he) 
  { 
    this->p_opp = he;
  }

  /*! Get the direction of the halfedge. */
  Arr_halfedge_direction direction () const
  {
    // Note that we use the LSB of the p_v pointer as a Boolean flag.
    if (_is_lsb_set (this->p_v))
      return (ARR_LEFT_TO_RIGHT);
    else
      return (ARR_RIGHT_TO_LEFT);
  }

  /*! Set the direction of the edge (and of its opposite halfedge). */
  void set_direction (Arr_halfedge_direction dir)
  {
    Halfedge*   opp = reinterpret_cast<Halfedge*> (this->p_opp);

    if (dir == ARR_LEFT_TO_RIGHT)
    {
      this->p_v = _set_lsb (this->p_v);
      opp->p_v = _clean_pointer (opp->p_v);
    }
    else
    {
      this->p_v = _clean_pointer (this->p_v);
      opp->p_v = _set_lsb (opp->p_v);
    }
  }

  /*! Get the previous halfedge along the chain (const version). */
  const Halfedge* prev () const
  {
    return (reinterpret_cast<const Halfedge*>(this->p_prev));
  }

  /*! Get the previous halfedge along the chain (const version). */
  Halfedge* prev ()
  {
    return (reinterpret_cast<Halfedge*>(this->p_prev));
  }

  /*! Set the previous halfedge along the chain. */
  void set_prev (Halfedge* he)
  {
    this->p_prev = he;
    he->p_next = this;
  }

  /*! Get the next halfedge along the chain (const version). */
  const Halfedge* next () const
  {
    return (reinterpret_cast<const Halfedge*>(this->p_next));
  }

  /*! Get the next halfedge along the chain (const version). */
  Halfedge* next ()
  {
    return (reinterpret_cast<Halfedge*>(this->p_next));
  }

  /*! Set the next halfedge along the chain. */
  void set_next (Halfedge* he)
  {
    this->p_next = he;
    he->p_prev = this;
  }

  /*! Get the target vertex (const version). */
  const Vertex* vertex () const 
  { 
    return (reinterpret_cast<const Vertex*>(_clean_pointer (this->p_v)));
  }

  /*! Get the target vertex (non-const version). */
  Vertex* vertex ()
  { 
    return (reinterpret_cast<Vertex*>(_clean_pointer (this->p_v)));
  }

  /*! Set the target vertex. */
  void set_vertex (Vertex* v)
  {
    // Set the vertex pointer, preserving the content of the LSB.
    if (_is_lsb_set (this->p_v))
      this->p_v = _set_lsb (v);
    else
      this->p_v = v;
  }

  /*! Check whether the halfedge lies on the boundary of an outer CCB. */
  bool is_on_outer_ccb () const
  {
    return (!_is_lsb_set (this->p_comp));
  }


  /*!
   * Get an incident outer CCB (const version).
   * \pre The edge does not lie on an inner CCB.
   */
  const Outer_ccb* outer_ccb () const
  {
    CGAL_precondition (! is_on_inner_ccb());
    return (reinterpret_cast<const Outer_ccb*>(this->p_comp));
  }

  /*!
   * Get an incident outer CCB (non-const version).
   * \pre The edge does not lie on an inner CCB.
   */
  Outer_ccb* outer_ccb ()
  {
    CGAL_precondition (! is_on_inner_ccb());
    return (reinterpret_cast<Outer_ccb*>(this->p_comp));
  }

  /*! Set the incident outer CCB. */
  void set_outer_ccb (Outer_ccb *oc)
  { 
    // Set the component pointer and reset its LSB.
    this->p_comp = oc;
  }

  /*! Check whether the halfedge lies on the boundary of an inner CCB. */
  bool is_on_inner_ccb () const
  {
    return (_is_lsb_set (this->p_comp));
  }

  /*!
   * Get an incident inner CCB (const version).
   * \pre The edge lies on an inner CCB.
   */
  const Inner_ccb* inner_ccb () const 
  {     
    CGAL_precondition (is_on_inner_ccb());
    return (reinterpret_cast<const Inner_ccb*>(_clean_pointer (this->p_comp)));
  }

  /*!
   * Get an incident inner CCB (non-const version).
   * \pre The edge lies on an inner CCB.
   */
  Inner_ccb* inner_ccb () 
  {
    CGAL_precondition (is_on_inner_ccb());
    return (reinterpret_cast<Inner_ccb*> (_clean_pointer (this->p_comp)));
  }

  /*! Set the incident inner CCB. */
  void set_inner_ccb (Inner_ccb *ic)
  { 
    // Set the component pointer and set its LSB.
    this->p_comp = _set_lsb (ic);
  }
};

/*! \class
 * The default arrangement DCEL face class.
 */
template <class V, class H, class F>
class Arr_face : public F,
                 public In_place_list_base<Arr_face<V,H,F> >
{
public:

  typedef F                            Base;
  typedef Arr_vertex<V,H,F>            Vertex;
  typedef Arr_halfedge<V,H,F>          Halfedge;
  typedef Arr_face<V,H,F>              Face;
  typedef Arr_outer_ccb<V,H,F>         Outer_ccb;
  typedef Arr_inner_ccb<V,H,F>         Inner_ccb;
  typedef Arr_isolated_vertex<V,H,F>   Isolated_vertex;

  typedef Inner_ccb                    Hole;
  
private:

  typedef Cast_function_object<void*,
                               Halfedge*>        _Ccb_to_halfedge_cast;
  // typedef Cast_function_object<const void*,
  //                              const Halfedge*>  _Const_ccb_to_halfedge_cast;
  typedef _Ccb_to_halfedge_cast _Const_ccb_to_halfedge_cast;

public:

  /*! Default constructor. */
  Arr_face()
  {}

  // Definition of the outer CCB iterators:
  typedef Iterator_project<typename F::Outer_ccb_iterator, 
                           _Ccb_to_halfedge_cast>   Outer_ccb_iterator;

  typedef Iterator_project<typename F::Outer_ccb_const_iterator,
                           _Const_ccb_to_halfedge_cast> 
                                                    Outer_ccb_const_iterator;

  /*! Get the number of outer CCBs the face has. */
  size_t number_of_outer_ccbs () const
  {
    return (this->outer_ccbs.size());
  }

  /*! Get an iterator for the first outer CCB of the face. */
  Outer_ccb_iterator outer_ccbs_begin()
  {
    return (this->outer_ccbs.begin());
  }

  /*! Get a past-the-end iterator for the outer CCBs inside the face. */
  Outer_ccb_iterator outer_ccbs_end()
  {
    return (this->outer_ccbs.end());
  }

  /*! Get an const iterator for the first outer CCB inside the face. */
  Outer_ccb_const_iterator outer_ccbs_begin() const
  {
    return (this->outer_ccbs.begin());
  }

  /*! Get a const past-the-end iterator for the outer CCBs inside the face. */
  Outer_ccb_const_iterator outer_ccbs_end() const
  {
    return (this->outer_ccbs.end());
  }

  /*! Add an outer CCB to the face. */
  void add_outer_ccb (Outer_ccb *oc, Halfedge *h)
  {
    oc->set_iterator (this->outer_ccbs.insert (this->outer_ccbs.end(), h));
    return;
  }

  /*! Erase an outer CCB of the face. */
  void erase_outer_ccb (Outer_ccb *oc)
  {
    this->outer_ccbs.erase (oc->iterator().current_iterator());
  }

  // Definition of the inner CCB iterators:
  typedef Iterator_project<typename F::Inner_ccb_iterator, 
                           _Ccb_to_halfedge_cast>   Inner_ccb_iterator;

  typedef Iterator_project<typename F::Inner_ccb_const_iterator,
                           _Const_ccb_to_halfedge_cast> 
                                                    Inner_ccb_const_iterator;

  typedef Inner_ccb_iterator                        Hole_iterator;
  typedef Inner_ccb_const_iterator                  Hole_const_iterator;
  
  /*! Get the number of inner CCBs the face has. */
  size_t number_of_inner_ccbs () const
  {
    return (this->inner_ccbs.size());
  }

  /*! Get an iterator for the first inner CCB of the face. */
  Inner_ccb_iterator inner_ccbs_begin()
  {
    return (this->inner_ccbs.begin());
  }

  /*! Get a past-the-end iterator for the inner CCBs inside the face. */
  Inner_ccb_iterator inner_ccbs_end()
  {
    return (this->inner_ccbs.end());
  }

  /*! Get an const iterator for the first inner CCB inside the face. */
  Inner_ccb_const_iterator inner_ccbs_begin() const
  {
    return (this->inner_ccbs.begin());
  }

  /*! Get a const past-the-end iterator for the inner CCBs inside the face. */
  Inner_ccb_const_iterator inner_ccbs_end() const
  {
    return (this->inner_ccbs.end());
  }

  /*! Add an inner CCB to the face. */
  void add_inner_ccb (Inner_ccb *ic, Halfedge *h)
  {
    ic->set_iterator (this->inner_ccbs.insert (this->inner_ccbs.end(), h));
    return;
  }

  /*! Erase an inner CCB of the face. */
  void erase_inner_ccb (Inner_ccb *ic)
  {
    this->inner_ccbs.erase (ic->iterator().current_iterator());
  }

  // Backward compatibility:
  size_t number_of_holes () const { return number_of_inner_ccbs(); }
  Hole_iterator holes_begin() { return inner_ccbs_begin(); }
  Hole_iterator holes_end() { return inner_ccbs_end(); }
  Hole_const_iterator holes_begin() const { return inner_ccbs_begin(); }
  Hole_const_iterator holes_end() const { return inner_ccbs_end(); }

  // Definition of the isloated vertices iterators:
  typedef I_Dereference_iterator<
    typename F::Isolated_vertex_iterator,
    Vertex,
    typename F::Isolated_vertex_iterator::difference_type,
    typename F::Isolated_vertex_iterator::iterator_category>
                                              Isolated_vertex_iterator;
  
  typedef I_Dereference_const_iterator<
    typename F::Isolated_vertex_const_iterator,
    typename F::Isolated_vertex_iterator,
    Vertex,
    typename F::Isolated_vertex_iterator::difference_type,
    typename F::Isolated_vertex_iterator::iterator_category>
                                              Isolated_vertex_const_iterator;

  /*! Get the number of isloated vertices inside the face. */
  size_t number_of_isolated_vertices() const
  {
    return (this->iso_verts.size());
  }

  /*! Get an iterator for the first isloated vertex inside the face. */
  Isolated_vertex_iterator isolated_vertices_begin()
  {
    return (this->iso_verts.begin());
  }

  /*! Get a past-the-end iterator for the isloated vertices inside the face. */
  Isolated_vertex_iterator isolated_vertices_end()
  {
    return (this->iso_verts.end());
  }

  /*! Get an const iterator for the first isloated vertex inside the face. */
  Isolated_vertex_const_iterator isolated_vertices_begin() const
  {
    return (this->iso_verts.begin());
  }

  /*! Get a const past-the-end iterator for the isloated vertices inside the
   * face. */
  Isolated_vertex_const_iterator isolated_vertices_end() const
  {
    return (this->iso_verts.end());
  }

  /*! Add an isloated vertex inside the face. */
  void add_isolated_vertex (Isolated_vertex *iv, Vertex* v)
  {
    iv->set_iterator (this->iso_verts.insert (this->iso_verts.end(), v));
    return;
  }

  /*! Erase an isloated vertex from the face. */
  void erase_isolated_vertex (Isolated_vertex *iv)
  {
    this->iso_verts.erase (iv->iterator().current_iterator());
    return;
  }

};

/*! \class
 * Representation of an outer CCB.
 */
template <class V, class H, class F>
class Arr_outer_ccb : public In_place_list_base<Arr_outer_ccb<V,H,F> >
{
public:

  typedef Arr_outer_ccb<V,H,F>               Self;
  typedef Arr_halfedge<V,H,F>                Halfedge;
  typedef Arr_face<V,H,F>                    Face;
  typedef typename Face::Outer_ccb_iterator  Outer_ccb_iterator;

private:

  Face               *p_f;    // The face the contains the CCB in its interior.
  Outer_ccb_iterator  iter;   // The outer CCB identifier.
  bool iter_is_not_singular;

public:

  /*! Default constructor. */
  Arr_outer_ccb () :
    p_f (NULL), iter_is_not_singular(false)
  {}

  /*! Copy constructor. */
  Arr_outer_ccb (const Arr_outer_ccb& other )
    : p_f (other.p_f), iter_is_not_singular(other.iter_is_not_singular)
  {
    if(other.iter_is_not_singular) {
      iter = other.iter;
    }
  }

  /*! Get a halfedge along the component (const version). */
  const Halfedge* halfedge () const
  {
    return (*iter);
  }

  /*! Get a halfedge along the component (non-const version). */
  Halfedge* halfedge ()
  {
    return (*iter);
  }

  /*! Set a representative halfedge for the component. */
  void set_halfedge (Halfedge *he)
  {
    *iter = he;
    return;
  }

  /*! Get the incident face (const version). */
  const Face* face () const
  {
    return (p_f);
  }

  /*! Get the incident face (non-const version). */
  Face* face ()
  {
    return (p_f);
  }

  /*! Set the incident face. */
  void set_face (Face* f)
  {
    p_f = f;
    return;
  }

  /*! Get the iterator (const version). */
  Outer_ccb_iterator iterator () const
  {
    CGAL_assertion(iter_is_not_singular);
    return (iter);
  }

  /*! Get the iterator (non-const version). */
  Outer_ccb_iterator iterator ()
  {
    CGAL_assertion(iter_is_not_singular);
    return (iter);
  }

  /*! Set the outer CCB iterator. */
  void set_iterator (Outer_ccb_iterator it)
  {
    iter = it;
    iter_is_not_singular = true;
    return;
  }
};

/*! \class
 * Representation of an inner CCB.
 */
template <class V, class H, class F>
class Arr_inner_ccb : public In_place_list_base<Arr_inner_ccb<V,H,F> >
{
public:

  typedef Arr_inner_ccb<V,H,F>               Self;
  typedef Arr_halfedge<V,H,F>                Halfedge;
  typedef Arr_face<V,H,F>                    Face;
  typedef typename Face::Inner_ccb_iterator  Inner_ccb_iterator;

private:

  Face               *p_f;    // The face the contains the CCB in its interior.
  Inner_ccb_iterator  iter;   // The inner CCB identifier.
  bool iter_is_not_singular;

public:

  /*! Default constructor. */
  Arr_inner_ccb () :
    p_f (NULL), iter_is_not_singular(false)
  {}

  /*! Copy constructor. */
  Arr_inner_ccb (const Arr_inner_ccb& other )
    : p_f (other.p_f), iter_is_not_singular(other.iter_is_not_singular)
  {
    if(other.iter_is_not_singular) {
      iter = other.iter;
    }
  }

  /*! Get a halfedge along the component (const version). */
  const Halfedge* halfedge () const
  {
    return (*iter);
  }

  /*! Get a halfedge along the component (non-const version). */
  Halfedge* halfedge ()
  {
    return (*iter);
  }

  /*! Set a representative halfedge for the component. */
  void set_halfedge (Halfedge *he)
  {
    *iter = he;
    return;
  }

  /*! Get the incident face (const version). */
  const Face* face () const
  {
    return (p_f);
  }

  /*! Get the incident face (non-const version). */
  Face* face ()
  {
    return (p_f);
  }

  /*! Set the incident face. */
  void set_face (Face* f)
  {
    p_f = f;
    return;
  }

  /*! Get the iterator (const version). */
  Inner_ccb_iterator iterator () const
  {
    CGAL_assertion(iter_is_not_singular);
    return (iter);
  }

  /*! Get the iterator (non-const version). */
  Inner_ccb_iterator iterator ()
  {
    CGAL_assertion(iter_is_not_singular);
    return (iter);
  }

  /*! Set the inner CCB iterator. */
  void set_iterator (Inner_ccb_iterator it)
  {
    iter = it;
    iter_is_not_singular = true;
    return;
  }
};

/*! \class
 * Representation of an isolated vertex.
 */
template <class V, class H, class F>
class Arr_isolated_vertex : 
public In_place_list_base<Arr_isolated_vertex<V,H,F> >
{
public:

  typedef Arr_isolated_vertex<V,H,F>                Self;
  typedef Arr_face<V,H,F>                           Face;
  typedef typename Face::Isolated_vertex_iterator   Isolated_vertex_iterator;

private:

  Face                        *p_f;     // The containing face.
  Isolated_vertex_iterator   iv_it;     // The isolated vertex identifier.
  bool iter_is_not_singular;

public:

  /*! Default constructor. */
  Arr_isolated_vertex ():
    p_f (NULL), iter_is_not_singular(false)
  {}

  /*! Copy constructor. */
  Arr_isolated_vertex (const Arr_isolated_vertex& other )
    : p_f (other.p_f), iter_is_not_singular(other.iter_is_not_singular)
  {
    if(other.iter_is_not_singular) {
      iv_it = other.iv_it;
    }
  }

  /*! Get the containing face (const version). */
  const Face* face () const
  {
    return (p_f);
  }

  /*! Get the containing face (non-const version). */
  Face* face ()
  {
    return (p_f);
  }

  /*! Set the incident face, the one that contains the isolated vertex. */
  void set_face (Face* f)
  {
    p_f = f;
    return;
  }

  /*! Get the isolated vertex iterator (const version). */
  Isolated_vertex_iterator iterator () const
  {
    CGAL_assertion(iter_is_not_singular);
    return (iv_it);
  }

  /*! Get the isolated vertex iterator (non-const version). */
  Isolated_vertex_iterator iterator ()
  {
    CGAL_assertion(iter_is_not_singular);
    return (iv_it);
  }

  /*! Set the isolated vertex iterator. */
  void set_iterator (Isolated_vertex_iterator iv)
  {
    iv_it = iv;
    iter_is_not_singular = true;
    return;
  }
};

/*! \class
 * The arrangement DCEL class.
 */
template <class V, class H, class F,
          class Allocator = CGAL_ALLOCATOR(int) >
class Arr_dcel_base
{
public:

  // Define the vertex, halfedge and face types.
  typedef Arr_dcel_base<V,H,F>        Self;
  typedef Arr_vertex<V,H,F>           Vertex;
  typedef Arr_halfedge<V,H,F>         Halfedge;
  typedef Arr_face<V,H,F>             Face;
  typedef Arr_outer_ccb<V,H,F>        Outer_ccb;
  typedef Arr_inner_ccb<V,H,F>        Inner_ccb;
  typedef Arr_isolated_vertex<V,H,F>  Isolated_vertex;

  typedef Inner_ccb                   Hole;
  
protected:

  // The vetices, halfedges and faces are stored in three in-place lists.
  typedef In_place_list<Vertex, false>           Vertex_list;
  typedef In_place_list<Halfedge, false>         Halfedge_list;
  typedef In_place_list<Face, false>             Face_list;
  typedef In_place_list<Outer_ccb, false>        Outer_ccb_list;
  typedef In_place_list<Inner_ccb, false>        Inner_ccb_list;
  typedef In_place_list<Isolated_vertex, false>  Iso_vert_list;

  // Vertex allocator.
  typedef typename Allocator::template rebind<Vertex>    Vertex_alloc_rebind;
  typedef typename Vertex_alloc_rebind::other            Vertex_allocator;

  // Halfedge allocator.
  typedef typename Allocator::template rebind<Halfedge>  Halfedge_alloc_rebind;
  typedef typename Halfedge_alloc_rebind::other          Halfedge_allocator;

  // Face allocator.
  typedef typename Allocator::template rebind<Face>      Face_alloc_rebind;
  typedef typename Face_alloc_rebind::other              Face_allocator;
  
  // Outer CCB allocator.
  typedef typename Allocator::template rebind<Outer_ccb> Out_ccb_alloc_rebind;
  typedef typename Out_ccb_alloc_rebind::other           Outer_ccb_allocator;

  // Inner CCB allocator.
  typedef typename Allocator::template rebind<Inner_ccb> In_ccb_alloc_rebind;
  typedef typename In_ccb_alloc_rebind::other            Inner_ccb_allocator;

  // Isolated vertex allocator.
  typedef typename Allocator::template rebind<Isolated_vertex>
                                                         Iso_vert_alloc_rebind;
  typedef typename Iso_vert_alloc_rebind::other          Iso_vert_allocator;

public:

  typedef typename Halfedge_list::size_type              Size;
  typedef typename Halfedge_list::size_type              size_type;
  typedef typename Halfedge_list::difference_type        difference_type;
  typedef typename Halfedge_list::difference_type        Difference;
  typedef std::bidirectional_iterator_tag                iterator_category;

protected:

  Vertex_list         vertices;             // The vertices container.
  Halfedge_list       halfedges;            // The halfedges container.
  Face_list           faces;                // The faces container.
  Outer_ccb_list      out_ccbs;             // The outer CCBs.
  Inner_ccb_list      in_ccbs;              // The inner CCBs.
  Iso_vert_list       iso_verts;            // The isolated vertices.

  Vertex_allocator    vertex_alloc;         // An allocator for vertices.
  Halfedge_allocator  halfedge_alloc;       // An allocator for halfedges.
  Face_allocator      face_alloc;           // An allocator for faces.
  Outer_ccb_allocator out_ccb_alloc;        // An allocator for outer CCBs.
  Inner_ccb_allocator in_ccb_alloc;         // An allocator for inner CCBs.
  Iso_vert_allocator  iso_vert_alloc;       // Allocator for isolated vertices.

public:

  // Definitions of iterators.
  typedef typename Vertex_list::iterator              Vertex_iterator;
  typedef typename Halfedge_list::iterator            Halfedge_iterator;
  typedef typename Face_list::iterator                Face_iterator;
  typedef CGAL::N_step_adaptor_derived<Halfedge_iterator, 2>
                                                      Edge_iterator;
  
  // Definitions of const iterators.
  typedef typename Vertex_list::const_iterator        Vertex_const_iterator;
  typedef typename Halfedge_list::const_iterator      Halfedge_const_iterator;
  typedef typename Face_list::const_iterator          Face_const_iterator;
  typedef CGAL::N_step_adaptor_derived<Halfedge_const_iterator, 2>
                                                      Edge_const_iterator;

private:

  // Copy constructor - not supported.
  Arr_dcel_base (const Self&) ;

  // Assignment operator - not supported.
  Self& operator= (const Self&);

public:
  /// \name Construction and destruction.
  //@{
  /*! Default constructor. */
  Arr_dcel_base ()
  {}
  
  /*! Destructor. */
  ~Arr_dcel_base ()
  {
    delete_all();
  }
  //@}

  /// \name The DCEL size.
  //@{
  /*! Get the number of DCEL vertices. */
  Size size_of_vertices () const
  { 
    return (vertices.size());
  }

  /*! Get the number of DCEL halfedges (twice the number of edges). */
  Size size_of_halfedges () const
  {
    return (halfedges.size());
  }

  /*! Get the number of DCEL faces. */
  Size size_of_faces() const
  {
    return (faces.size());
  }

  /*! Get the number of outer CCBs. */
  Size size_of_outer_ccbs() const
  {
    return (out_ccbs.size());
  }

  /*! Get the number of inner CCBs. */
  Size size_of_inner_ccbs() const
  {
    return (in_ccbs.size());
  }

  /*! Get the number of isolated vertices. */
  Size size_of_isolated_vertices () const
  {
    return (iso_verts.size());
  }
  //@}

  /// \name Obtaining iterators.
  //@{
  Vertex_iterator   vertices_begin()  { return vertices.begin(); }
  Vertex_iterator   vertices_end()    { return vertices.end(); }
  Halfedge_iterator halfedges_begin() { return halfedges.begin();}
  Halfedge_iterator halfedges_end()   { return halfedges.end(); }
  Face_iterator     faces_begin()     { return faces.begin(); }
  Face_iterator     faces_end()       { return faces.end(); }
  Edge_iterator     edges_begin()     { return halfedges.begin(); }
  Edge_iterator     edges_end()       { return halfedges.end(); }
  //@}

  /// \name Obtaining constant iterators.
  //@{
  Vertex_const_iterator   vertices_begin() const { return vertices.begin(); }
  Vertex_const_iterator   vertices_end() const { return vertices.end(); }
  Halfedge_const_iterator halfedges_begin() const { return halfedges.begin(); }
  Halfedge_const_iterator halfedges_end() const { return halfedges.end(); }
  Face_const_iterator     faces_begin() const { return faces.begin(); }
  Face_const_iterator     faces_end() const { return faces.end(); }
  Edge_const_iterator     edges_begin() const { return halfedges.begin(); }
  Edge_const_iterator     edges_end() const { return halfedges.end(); }
  //@}

  // \name Creation of new DCEL features.
  //@{
  /*! Create a new vertex. */
  Vertex* new_vertex()
  {
    Vertex     *v = vertex_alloc.allocate (1);

    vertex_alloc.construct (v, Vertex());
    vertices.push_back (*v);
    return v;
  }
  
  /*! Create a new pair of opposite halfedges. */
  Halfedge* new_edge() 
  {
    // Create two new halfedges.
    Halfedge   *h1 = _new_halfedge ();
    Halfedge   *h2 = _new_halfedge ();

    // Pair them together.
    h1->set_opposite (h2);
    h2->set_opposite (h1);

    return (h1);
  }

  /*! Create a new face. */
  Face* new_face()
  {
    Face       *f = face_alloc.allocate (1);
    
    face_alloc.construct (f, Face());
    faces.push_back (*f);
    return (f);
  }

  /*! Create a new outer CCB. */
  Outer_ccb* new_outer_ccb ()
  {
    Outer_ccb  *oc = out_ccb_alloc.allocate (1);
    out_ccb_alloc.construct (oc, Outer_ccb());
    out_ccbs.push_back (*oc);
    return (oc);
  }

  /*! Create a new inner CCB. */
  Inner_ccb* new_inner_ccb ()
  {
    Inner_ccb  *ic = in_ccb_alloc.allocate (1);
    
    in_ccb_alloc.construct (ic, Inner_ccb());
    in_ccbs.push_back (*ic);
    return (ic);
  }

  /*! Create a new isolated vertex. */
  Isolated_vertex* new_isolated_vertex ()
  {
    Isolated_vertex  *iv = iso_vert_alloc.allocate (1);
    
    iso_vert_alloc.construct (iv, Isolated_vertex());
    iso_verts.push_back (*iv);
    return (iv);
  }
  //@}

  /// \name Deletion of DCEL features.
  //@{
  /*! Delete an existing vertex. */
  void delete_vertex (Vertex *v)
  {
    vertices.erase (v);
    vertex_alloc.destroy (v);
    vertex_alloc.deallocate (v,1);
  }
  
  /*! Delete an existing pair of opposite halfedges. */
  void delete_edge (Halfedge *h) 
  {
    Halfedge   *h_opp = h->opposite();

    _delete_halfedge (h);
    _delete_halfedge (h_opp);
  }

  /*! Delete an existing face. */
  void delete_face(Face *f)
  {
    faces.erase (f);
    face_alloc.destroy (f);
    face_alloc.deallocate (f, 1);
  }

  /*! Delete an existing outer CCB. */
  void delete_outer_ccb (Outer_ccb *oc)
  {
    out_ccbs.erase (oc);
    out_ccb_alloc.destroy (oc);
    out_ccb_alloc.deallocate (oc, 1);
  }

  /*! Delete an existing inner CCB. */
  void delete_inner_ccb (Inner_ccb *ic)
  {
    in_ccbs.erase (ic);
    in_ccb_alloc.destroy (ic);
    in_ccb_alloc.deallocate (ic, 1);
  }

  /*! Delete an existing isolated vertex. */
  void delete_isolated_vertex (Isolated_vertex *iv)
  {
    iso_verts.erase (iv);
    iso_vert_alloc.destroy (iv);
    iso_vert_alloc.deallocate (iv, 1);
  }
  
  /*! Delete all DCEL features. */
  void delete_all() 
  {
    // Free all vertices.
    Vertex_iterator    vit = vertices.begin(), v_curr;

    while (vit != vertices.end())
    {
      v_curr = vit;
      ++vit;
      delete_vertex (&(*v_curr));
    }

    // Free all halfedges.
    Halfedge_iterator  hit = halfedges.begin(), h_curr;

    while (hit != halfedges.end())
    {
      h_curr = hit;
      ++hit;
      _delete_halfedge (&(*h_curr));
    }

    // Free all faces.
    Face_iterator      fit = faces.begin(), f_curr;

    while (fit != faces.end())
    {
      f_curr = fit;
      ++fit;
      delete_face (&(*f_curr));
    }

    // Free all outer CCBs.
    typename Outer_ccb_list::iterator   ocit = out_ccbs.begin(), oc_curr;

    while (ocit != out_ccbs.end())
    {
      oc_curr = ocit;
      ++ocit;
      delete_outer_ccb (&(*oc_curr));
    }

    // Free all inner CCBs.
    typename Inner_ccb_list::iterator   icit = in_ccbs.begin(), ic_curr;

    while (icit != in_ccbs.end())
    {
      ic_curr = icit;
      ++icit;
      delete_inner_ccb (&(*ic_curr));
    }

    // Free all isolated vertices.
    typename Iso_vert_list::iterator   ivit = iso_verts.begin(), iv_curr;

    while (ivit != iso_verts.end())
    {
      iv_curr = ivit;
      ++ivit;
      delete_isolated_vertex (&(*iv_curr));
    }
  }
  //@}

  /*!
   * Assign our DCEL the contents of another DCEL.
   */
  void assign (const Self& dcel)
  {
    // Clear the current contents of the DCEL.
    delete_all();

    // Create duplicated of the DCEL features and map the features of the
    // given DCEL to their corresponding duplicates.
    typedef std::map<const Vertex*, Vertex*>                    Vertex_map;
    typedef std::map<const Halfedge*, Halfedge*>                Halfedge_map;
    typedef std::map<const Face*, Face*>                        Face_map;
    typedef std::map<const Outer_ccb*, Outer_ccb*>              Outer_ccb_map;
    typedef std::map<const Inner_ccb*, Inner_ccb*>              Inner_ccb_map;
    typedef std::map<const Isolated_vertex*, Isolated_vertex*>  Iso_vert_map;

    Vertex_map                v_map;
    Vertex_const_iterator     vit;
    Vertex                   *dup_v;

    for (vit = dcel.vertices_begin(); vit != dcel.vertices_end(); ++vit)
    {
      dup_v = new_vertex();
      dup_v->assign (*vit);
      v_map.insert (typename Vertex_map::value_type (&(*vit), dup_v));
    }

    Halfedge_map              he_map;
    Halfedge_const_iterator   hit;
    Halfedge                 *dup_h;

    for (hit = dcel.halfedges_begin(); hit != dcel.halfedges_end(); ++hit)
    {
      dup_h = _new_halfedge();
      dup_h->assign (*hit);
      he_map.insert (typename Halfedge_map::value_type(&(*hit), dup_h));
    }

    Face_map                  f_map;
    Face_const_iterator       fit;
    Face                     *dup_f;

    for (fit = dcel.faces_begin(); fit != dcel.faces_end(); ++fit)
    {
      dup_f = new_face();
      dup_f->assign (*fit);
      f_map.insert (typename Face_map::value_type(&(*fit), dup_f));
    }

    Outer_ccb_map                            oc_map;
    typename Outer_ccb_list::const_iterator  ocit;
    Outer_ccb                               *dup_oc;

    for (ocit = dcel.out_ccbs.begin(); ocit != dcel.out_ccbs.end(); ++ocit)
    {
      dup_oc = new_outer_ccb();
      oc_map.insert (typename Outer_ccb_map::value_type(&(*ocit), dup_oc));
    }

    Inner_ccb_map                            ic_map;
    typename Inner_ccb_list::const_iterator  icit;
    Inner_ccb                               *dup_ic;

    for (icit = dcel.in_ccbs.begin(); icit != dcel.in_ccbs.end(); ++icit)
    {
      dup_ic = new_inner_ccb();
      ic_map.insert (typename Inner_ccb_map::value_type(&(*icit), dup_ic));
    }

    Iso_vert_map                            iv_map;
    typename Iso_vert_list::const_iterator  ivit;
    Isolated_vertex                        *dup_iv;

    for (ivit = dcel.iso_verts.begin(); ivit != dcel.iso_verts.end(); ++ivit)
    {
      dup_iv = new_isolated_vertex();
      iv_map.insert (typename Iso_vert_map::value_type(&(*ivit), dup_iv));
    }

    // Update the vertex records.
    const Vertex             *v;
    const Halfedge           *h;
    const Face               *f;
    const Outer_ccb          *oc;
    const Inner_ccb          *ic;
    const Isolated_vertex    *iv;
    
    for (vit = dcel.vertices_begin(); vit != dcel.vertices_end(); ++vit)
    {
      v = &(*vit);
      dup_v = (v_map.find (v))->second;

      if (v->is_isolated())
      {
        // Isolated vertex - set its information.
        iv = v->isolated_vertex();
        dup_iv = (iv_map.find (iv))->second;

        dup_v->set_isolated_vertex (dup_iv);
      }
      else
      {
        // Regular vertex - set its incident halfedge.
        h = v->halfedge();
        dup_h = (he_map.find (h))->second;

        dup_v->set_halfedge (dup_h);
      }
    }

    // Update the halfedge records.
    const Halfedge           *opp, *prev, *next;
    Halfedge                 *dup_opp, *dup_prev, *dup_next;

    for (hit = dcel.halfedges_begin(); hit != dcel.halfedges_end(); ++hit)
    {
      h = &(*hit);
      v = h->vertex();
      opp = h->opposite();
      prev = h->prev();
      next = h->next();

      dup_h = (he_map.find (h))->second;
      dup_v = (v_map.find (v))->second;
      dup_opp = (he_map.find (opp))->second;
      dup_prev = (he_map.find (prev))->second;
      dup_next = (he_map.find (next))->second;

      dup_h->set_vertex (dup_v);
      dup_h->set_opposite (dup_opp);
      dup_h->set_prev (dup_prev);
      dup_h->set_next (dup_next);
      dup_h->set_direction (h->direction());

      if (h->is_on_inner_ccb())
      {
        // The halfedge lies on an inner CCB - set its inner CCB record.
        ic = h->inner_ccb();
        dup_ic = (ic_map.find (ic))->second;
        dup_h->set_inner_ccb (dup_ic);
      }
      else
      {
        // The halfedge lies on an outer CCB - set its outer CCB record.
        oc = h->outer_ccb();
        dup_oc = (oc_map.find (oc))->second;
        dup_h->set_outer_ccb (dup_oc);
      }
    }

    // Update the face records, along with the CCB records and isolated vertex
    // records.
    typename Face::Outer_ccb_const_iterator        out_ccb_it;
    typename Face::Inner_ccb_const_iterator        in_ccb_it;
    typename Face::Isolated_vertex_const_iterator  iso_vert_it;
    const Halfedge                      *hccb;
    const Vertex                        *iso_vert;
    Halfedge                            *dup_hccb;
    Vertex                              *dup_iso_vert;

    for (fit = dcel.faces_begin(); fit != dcel.faces_end(); ++fit)
    {
      f = &(*fit);
      dup_f = (f_map.find (f))->second;
      dup_f->set_unbounded (f->is_unbounded());
      dup_f->set_fictitious (f->is_fictitious());

      // Assign the outer CCBs of the face.
      for (out_ccb_it = f->outer_ccbs_begin();
           out_ccb_it != f->outer_ccbs_end(); ++out_ccb_it)
      {
        hccb = *out_ccb_it;

        dup_hccb = (he_map.find (hccb))->second;
        dup_oc = dup_hccb->outer_ccb();

        dup_oc->set_face (dup_f);
        dup_f->add_outer_ccb (dup_oc, dup_hccb);
      }

      // Assign the inner CCBs of the face.
      for (in_ccb_it = f->inner_ccbs_begin();
           in_ccb_it != f->inner_ccbs_end(); ++in_ccb_it)
      {
        hccb = *in_ccb_it;

        dup_hccb = (he_map.find (hccb))->second;
        dup_ic = dup_hccb->inner_ccb();

        dup_ic->set_face (dup_f);
        dup_f->add_inner_ccb (dup_ic, dup_hccb);
      }

      // Assign the isolated vertices.
      for (iso_vert_it = f->isolated_vertices_begin();
           iso_vert_it != f->isolated_vertices_end(); ++iso_vert_it)
      {
        iso_vert = &(*iso_vert_it);

        dup_iso_vert = (v_map.find (iso_vert))->second;
        dup_iv = dup_iso_vert->isolated_vertex();

        dup_iv->set_face (dup_f);
        dup_f->add_isolated_vertex (dup_iv, dup_iso_vert);
      }
    }

    return;
  }

protected:

  /*! Create a new halfedge. */
  Halfedge * _new_halfedge ()
  {
    Halfedge   *h = halfedge_alloc.allocate (1);

    halfedge_alloc.construct (h, Halfedge());
    halfedges.push_back (*h);
    return (h);
  }

  /*! Delete an existing halfedge. */
  void _delete_halfedge (Halfedge* h)
  {
    halfedges.erase (h);
    halfedge_alloc.destroy (h);
    halfedge_alloc.deallocate (h, 1);
  }
};

} //namespace CGAL

#endif 
