// Copyright (c) 1997  Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you may redistribute it under
// the terms of the Q Public License version 1.0.
// See the file LICENSE.QPL distributed with CGAL.
//
// 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.
//
// $Source: /CVSROOT/CGAL/Packages/Planar_map/include/CGAL/Pm_default_dcel.h,v $
// $Revision: 1.10 $ $Date: 2004/07/08 14:26:38 $
// $Name:  $
//
// Author(s)     : Iddo Hanniel <hanniel@math.tau.ac.il>
//                 Oren Nechushtan <theoren@math.tau.ac.il>
#ifndef CGAL_PM_DEFAULT_DCEL_H
#define CGAL_PM_DEFAULT_DCEL_H

#include <CGAL/basic.h>
#include <list>
#include <map>
#include <CGAL/N_step_adaptor.h>
#include <CGAL/In_place_list.h>
#include <CGAL/HalfedgeDS_iterator.h>

CGAL_BEGIN_NAMESPACE

/*! Base vertex class */
template <class Pt>
class Pm_vertex_base {
protected:
  /*! An incident halfedge pointing at the vertex */
  void * hdg;
  Pt pt;

public:
  typedef Pt           Point;
  
  Pm_vertex_base() {}
  Pm_vertex_base(const Pt & p) : pt(p) {}
  
  virtual ~Pm_vertex_base() {}

  void * halfedge() { return hdg; }
  const void * halfedge() const { return hdg; }
  void set_halfedge(void * h) { hdg = h; }
  
  Point & point() { return pt; }
  const Point & point() const { return pt; }
  
  void set_point(const Point & p) { pt = p; }

  // assign function for non-connectivity data
  virtual void assign(const Pm_vertex_base<Pt> & v)
  {
    pt = v.pt;
  }
};

/*! Base halfedge class */
template <class X_curve>
class Pm_halfedge_base {
public:
  typedef X_curve Curve;

  /*! Parameterless Constructor */
  Pm_halfedge_base() {}

  /*! Constructor */
  Pm_halfedge_base(const X_curve & c) : cv(c) {}

  /*! Destructor */
  virtual ~Pm_halfedge_base() {}

  /*! \brief obtains the opposite halfedge */
  void * opposite() { return opp; }
  const void * opposite() const { return opp; }

  /*! \brief obtains the next halfedge along the face */
  void * next() { return nxt; }
  const void * next() const { return nxt; }
  
  /*! \brief sets the opposite halfedge */
  void set_opposite(void * h) { opp = h; }

  /*! \brief sets the next halfedge along the face */
  void set_next(void * h) { nxt = h; }
  
  /*! \brief obtains the incident target vertex */
  void * vertex() { return v; }
  const void * vertex() const { return v; }
  
  /*! \brief obtains the face to the left */
  void * face() { return f; }
  const void * face() const { return f; }

  /*! \brief sets the incident target vertex */
  void set_vertex(void * _v) { v = _v; }

  /*! \brief sets the face to the left */
  void set_face(void * _f) { f = _f; }

  /*! \brief obtains the geometric x-monotone curve */
  Curve & curve() { return cv; }
  const Curve & curve() const { return cv; }

  /*! \brief sets the geometric x-monotone curve */
  void set_curve(const X_curve& c) { cv = c; }

  /*! assign function for non-connectivity data */
  virtual void assign(const Pm_halfedge_base<X_curve> &e)
  {
    cv = e.cv;
  }

protected:
  void * opp;
  void * nxt;
  
  void * v;     // target
  void * f;     // face
  
  X_curve cv;
};

/*! Base face class */
class Pm_face_base {
public:
  typedef std::list<void*> Holes_container; 
  typedef Holes_container::iterator Holes_iterator; 
  typedef Holes_container::const_iterator Holes_const_iterator;

  /*! Parameterless Constructor */
  Pm_face_base() : holes() {};

  /*! Destructor */
  virtual ~Pm_face_base() {}

  void * halfedge() { return hdg;}
  const void * halfedge() const { return hdg;}

  void set_halfedge(void * h) {hdg = h;}

  Holes_iterator  holes_begin() {return holes.begin();}
  Holes_iterator  holes_end() {return holes.end();}

  Holes_const_iterator  holes_begin() const {return holes.begin();}
  Holes_const_iterator  holes_end() const {return holes.end();}

  void add_hole(void * halfedge_ptr) { holes.push_back(halfedge_ptr); }

  void erase_hole(Holes_iterator hit) { holes.erase(hit); }
    
  void erase_holes(Holes_iterator first, Holes_iterator last) {
    holes.erase(first,last);
  }

  // assign function for non-connectivity data
  virtual void assign(const Pm_face_base & f)
  { 
    // The reason we do not assign anything here is because
    // we can't copy pointers of a face from another Pm_dcel.
    // The assign function of Pm_dcel does all the necessary updates.

    (void) f; // We avoid the `unused parameter' warning.
  }
  
private:
  void * hdg;
  Holes_container holes;
};

// Forward declarations:
template <class V, class H, class F> class _Pm_Vertex;
template <class V, class H, class F> class _Pm_Halfedge;
template <class V, class H, class F> class _Pm_Face;

/*! The vertex class */
template <class V, class H, class F>
class _Pm_Vertex : public V,
                   public In_place_list_base< _Pm_Vertex<V,H,F> >
{
public:
  typedef V                     Base;
  typedef _Pm_Vertex<V,H,F>     Vertex;
  typedef _Pm_Halfedge<V,H,F>   Halfedge;
  typedef _Pm_Face<V,H,F>       Face;

  _Pm_Vertex() {}
  // _Pm_Vertex(const Point & p) : V(p) {}

  /*! \brief obtains an incident halfedge */
  Halfedge * halfedge() { return (Halfedge*)(V::halfedge()); }

  /*! \brief obtains an incident halfedge */
  const Halfedge * halfedge() const
  {
    return (const Halfedge*)(V::halfedge());
  }

  /*! \brief sets an incident halfedge */
  void set_halfedge(Halfedge * h) { V::set_halfedge(h); }

#if 0
  // Apparently the implementation of the In_place_list requires a copy
  // constructor of the value type!
protected:
  // forbid copy constructor and assignment (only derived classes can use them)

  /*! Copy Constructor */
  _Pm_Vertex(const _Pm_Vertex & v) {}

  /*! Assignment operator */
  _Pm_Vertex & operator=(const _Pm_Vertex &) { return *this; }
#endif
};

/*! The halfdege class */
template <class V, class H, class F>
class _Pm_Halfedge : public  H,
                     public  In_place_list_base< _Pm_Halfedge<V,H,F> >
{
public:
  typedef H                     Base;
  typedef _Pm_Vertex<V,H,F>     Vertex;
  typedef _Pm_Halfedge<V,H,F>   Halfedge;
  typedef _Pm_Face<V,H,F>       Face;

  _Pm_Halfedge() : H() {}  
  //_Pm_Halfedge( const Curve& c) : H(c) {}

  /*! \brief obtains the opposite halfedge */
  Halfedge * opposite() { return (Halfedge*)(H::opposite()); }
  
  //in the future will probably be implemented in a max base
  //    const Halfedge* prev()     const {return (const Halfedge*)(H::prev());}

  /*! \brief obtains the base halfedge */
  Vertex * vertex() { return (Vertex*)(H::vertex()); }

  /*! \brief obtains the base halfedge */
  const Vertex * vertex() const {return (const Vertex*)(H::vertex()); }

  /*! \brief obtains the incident base face */
  Face * face() { return (Face*)(H::face()); }
  
  /*! \brief obtains the incident base face */
  const Face * face() const {return (const Face*)(H::face()); }
  
  /*! \brief obtains the next halfedge along the face */
  const Halfedge * next() const { return (const Halfedge*)(H::next()); }

  /*! \brief obtains the next halfedge along the face. */
  Halfedge * next() { return (Halfedge*)(H::next()); }
  
  //in the future will probably be implemented in a max base
  // const Halfedge* prev() const {return (const Halfedge*)(H::prev()); }

  /*! \brief obtains the opposite halfedge */
  const Halfedge * opposite() const
  {
    return (const Halfedge*)(H::opposite());
  }

  /*! \brief sets the base vertex */
  void set_vertex(Vertex * ve) { H::set_vertex(ve); }

  /*! \brief sets the next halfedge along the face */
  void set_next(Halfedge * h) { H::set_next(h); }

  /*! \brief sets the incident base face */
  void set_face(Face * face) { H::set_face(face); }
  
//private:
  /*! \brief sets the opposite halfedge */
  void set_opposite(void * h) { H::set_opposite(h); }

#if 0
  // Apparently the implementation of the In_place_list requires a copy
  // constructor of the value type!
protected:
  //forbid copy constructor and assignment (only derived classes can use them)

  /*! Copy Constructor */
  _Pm_Halfedge(const _Pm_Halfedge &) {}

  /*! Assignment Operator */
  _Pm_Halfedge & operator=(const _Pm_Halfedge &) { return *this; }
#endif
};

/*! The face class */
template <class V, class H, class F>
class _Pm_Face : public F,
                 public In_place_list_base< _Pm_Face<V,H,F> >
{
public:
  typedef F                     Base;
  typedef _Pm_Vertex<V,H,F>     Vertex;
  typedef _Pm_Halfedge<V,H,F>   Halfedge;
  typedef _Pm_Face<V,H,F>       Face;

  /*! Parameterless Constructor */
  _Pm_Face() {}  
  
  /*! \brief obtains an incident halfedge */
  Halfedge * halfedge() { return (Halfedge*)(F::halfedge()); }

  /*! \brief obtains an incident halfedge */
  const Halfedge * halfedge() const
  {
    return (const Halfedge*)(F::halfedge());
  }

  /*! \brief sets an incident halfedge */
  void set_halfedge(Halfedge * h) { F::set_halfedge(h); }

  typedef I_HalfedgeDS_iterator< typename F::Holes_iterator, 
    Halfedge*, 
    typename F::Holes_iterator::difference_type,
    typename F::Holes_iterator::iterator_category>       Holes_iterator;

  typedef I_HalfedgeDS_const_iterator<
    typename F::Holes_const_iterator, 
    typename F::Holes_iterator,
    const Halfedge*,
    typename F::Holes_const_iterator::difference_type,
    typename F::Holes_const_iterator::iterator_category> Holes_const_iterator;

  /*! \brief adds a hole */
  void add_hole(Halfedge * h) { F::add_hole(h); }

  /*! \brief erases a hole */
  void erase_hole(Holes_iterator hit)
  {
    F::erase_hole(hit.current_iterator());
  }

  /*! \brief erases a range of holes */
  void erase_holes(Holes_iterator first, Holes_iterator last) 
  {
    F::erase_holes(first.current_iterator(), last.current_iterator());
  }

  Holes_iterator holes_begin() {return F::holes_begin(); }
  Holes_iterator holes_end() {return F::holes_end(); }

  Holes_const_iterator holes_begin() const {return F::holes_begin(); }
  Holes_const_iterator holes_end() const {return F::holes_end(); }
  
#if 0
  // Apparently the implementation of the In_place_list requires a copy
  // constructor of the value type!
protected:
  //forbid copy constructor and assignment (only derived classes can use them)

  /*! Copy Constructor */
  _Pm_Face(const _Pm_Face &) {}

  /*! Assignment Operator */
  _Pm_Face & operator=(const _Pm_Face &) { return * this; }
#endif
};

/*! A Dcel Class Using Lists */
template <class V, class H, class F>
class Pm_dcel {
public:
  typedef Pm_dcel<V,H,F>        Self;
  typedef _Pm_Vertex<V,H,F>     Vertex;
  typedef _Pm_Halfedge<V,H,F>   Halfedge;
  typedef _Pm_Face<V,H,F>       Face;
  
protected:
  // Three managed in-place lists for the elements. 
  typedef In_place_list<Vertex,false>   Vertex_list;
  typedef In_place_list<Halfedge,false> Halfedge_list;
  typedef In_place_list<Face,false>     Face_list;

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;
  Halfedge_list halfedges;
  Face_list faces;

public:
  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<Halfedge_iterator, 2>    Edge_iterator;
  
  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<Halfedge_const_iterator,2>
    Edge_const_iterator;

  /*! Parameterless Constructor */
  Pm_dcel() {}

private:
  // Forbid copy constructor and assignment (will be implemented later).

  /*! Copy Constructor */
  Pm_dcel(const Self & dcel)
  {
    // vertices = dcel.vertices;
    // halfedges = dcel.halfedges;
    // faces = dcel.faces;
  }

  /*! Assignment operator */
  Self & operator=(const Self & dcel)
  {
    // vertices = dcel.vertices;
    // halfedges = dcel.halfedges;
    // faces = dcel.faces;
    return *this;
  }
  
public:
  /*! Destructor */
  ~Pm_dcel() { delete_all(); }

public:
  Size size_of_vertices() const  { return vertices.size(); }
  Size size_of_halfedges() const { return halfedges.size(); }
  Size size_of_faces() const     { return faces.size(); }

  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(); }  

  // The constant iterators and circulators.

  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(); }  
  
  // Insertion
  //
  // The following operations just allocate a new element of that type.
  // Halfedges are always allocated in pairs of opposite halfedges. The
  // opposite pointers are automatically set.
  
  Vertex* new_vertex() {
    Vertex* v = new Vertex;
    vertices.push_back( *v);
    return v;
  }
  
  Vertex* new_vertex( const Vertex* w) {
    Vertex* v = new Vertex(*w);
    vertices.push_back( *v);
    return v;
  }
  
  /*
  Vertex* new_vertex( const Point& p) {
    Vertex* v = new Vertex(p);
    vertices.push_back( *v);
    return v;
  }
  */

  Halfedge * new_halfedge() {
    Halfedge * h = new Halfedge;
    halfedges.push_back(*h);
    return h;
  }

  Halfedge * new_halfedge(const Halfedge * he) {
    Halfedge * h = new Halfedge( *he);
    halfedges.push_back(*h);
    return h;
  }

  Halfedge * new_edge() {
    // creates a new pair of opposite halfedges.
    Halfedge * h = new_halfedge();
    Halfedge * g = new_halfedge();
    h->H::set_opposite(g);
    g->H::set_opposite(h);
    return h;
  }

  Halfedge * new_edge( const Halfedge * he) {
    Halfedge * h = new_halfedge(he);
    Halfedge * g = new_halfedge(he->opposite());
    h->H::set_opposite(g);
    g->H::set_opposite(h);
    return h;
  }
  
  /*
    Halfedge* new_edge(const Curve& c) {
    Halfedge* h = new Halfedge(c);
    Halfedge* g = new Halfedge(c);  //maybe change to flip??
    //    h->H::set_twin(g);
    //g->H::set_twin(h);
    h->H::set_opposite(g);
    g->H::set_opposite(h);

    halfedges.push_back( *h);
    halfedges.push_back( *g);
    return h;
  }*/

  Face * new_face() {
    Face * f = new Face;
    faces.push_back(*f);
    return f;
  }
  
  Face * new_face(const Face * g) {
    Face * f = new Face(*g);
    faces.push_back(*f);
    return f;
  }
  
  // Removal
  //
  // The following operations erase an element referenced by a pointer.
  // Halfedges are always deallocated in pairs of opposite halfedges. Erase
  // of single elements is optional. The deletion of all is mandatory.

  void delete_vertex(Vertex * v) {
    vertices.erase(v);
    delete v;
  }
  
  void delete_halfedge(Halfedge * h) {
    halfedges.erase(h);
    delete h;
  }
    
  void delete_edge(Halfedge * h) {
    // deletes the pair of opposite halfedges h.
    Halfedge * g = h->opposite();
    delete_halfedge(h);
    delete_halfedge(g);
  }

  void delete_face(Face * f) {
    faces.erase(f);
    delete f;
  }
  
  void delete_all() {
    for (Vertex_iterator vi = vertices.begin(); vi != vertices.end();) {
      Vertex_iterator curr = vi;
      ++vi;
      delete_vertex(&(*curr));
    }

    for (Halfedge_iterator hi = halfedges.begin(); hi != halfedges.end();) {
      Halfedge_iterator curr = hi;
      ++hi;
      delete_halfedge(&(*curr));
    }

    for (Face_iterator fi = faces.begin(); fi != faces.end();) {
      Face_iterator curr = fi;
      ++fi;
      delete_face(&(*curr));
    }

    // vertices.destroy();
    // halfedges.destroy();
    // faces.destroy();
  }
  
  /*! returns the unbounded face in the assigned map */
  void * assign(const Self & d, void * u_face)
  {
    //typedef std::map<Vertex_list::iterator, Vertex_list::iterator> VertexMap;
    //typedef std::map<Halfedge_list::iterator, 
    //                 Halfedge_list::iterator> HalfedgeMap;
    //typedef std::map<Face_list::iterator, Face_list::iterator> FaceMap;
    typedef std::map<void*, void*> ConnectMap;

    delete_all();

    ConnectMap vm, hm, fm;
    //VertexMap vm;
    //HalfedgeMap hm;
    //FaceMap fm;

    Vertex_const_iterator vit;
    Halfedge_const_iterator hit;
    Face_const_iterator fit;

    for (vit = d.vertices_begin(); vit != d.vertices_end(); vit++)
    {
      Vertex * nv = new_vertex();
      nv->assign(*vit);
      vm.insert(ConnectMap::value_type((void*)&(*vit), (void*)nv));
    }

    for (hit = d.halfedges_begin(); hit != d.halfedges_end(); hit++)
    {
      Halfedge * nh = new_halfedge();
      nh->assign(*hit);
      hm.insert(ConnectMap::value_type((void*)(&(*hit)), (void*)nh));
    }

    for (fit = d.faces_begin(); fit != d.faces_end(); fit++)
    {
      Face * nf = new_face();
      nf->assign(*fit);
      fm.insert(ConnectMap::value_type((void*)&(*fit), (void*)nf));
    }

    // update pointers
    for (vit = d.vertices_begin(); vit != d.vertices_end(); vit++)
    {
      void *he, *nhe, *nv, *v;
      v = (void*)(&(*vit));
      nv = (void*)(vm.find(v)->second);
      he = (void*)vit->halfedge();
      nhe = (void*)(hm.find(he)->second);
      ((Vertex*)nv)->set_halfedge((Halfedge*)nhe);
    }

    for (hit = d.halfedges_begin(); hit != d.halfedges_end(); hit++)
    {
      void *he, *nhe, *v, *nv, *f, *nf, *op, *nop, *xt, *nxt;
      he = (void*)(&(*hit));
      nhe = hm.find(he)->second;
      v = (void*)hit->vertex();
      f = (void*)hit->face();
      op = (void*)hit->opposite();
      xt = (void*)hit->next();

      nv = vm.find(v)->second;
      nf = fm.find(f)->second;
      nop = hm.find(op)->second;
      nxt = hm.find(xt)->second;

      ((Halfedge*)nhe)->set_vertex((Vertex*)nv);
      ((Halfedge*)nhe)->set_face((Face*)nf);
      ((Halfedge*)nhe)->set_opposite((Halfedge*)nop);
      ((Halfedge*)nhe)->set_next((Halfedge*)nxt);
    }

    for (fit = d.faces_begin(); fit != d.faces_end(); fit++)
    {
      void *f, *nf, *he, *nhe, *h, *nh;
      typename Face::Holes_const_iterator holes;
      f = (void*)(&(*fit));
      nf = fm.find(f)->second;
      he = (void*)fit->halfedge();
      if (he != NULL)
        nhe = hm.find(he)->second;
      else
        nhe = NULL;
      ((Face*)nf)->set_halfedge((Halfedge*)nhe);

      for (holes = fit->holes_begin(); holes != fit->holes_end(); holes++)
      {
        h = (void*)(*holes);
        nh = hm.find(h)->second;
        ((Face*)nf)->add_hole((Halfedge*)nh);
      }
    }
    return fm.find(u_face)->second;
  }
};

/*!
 * DEFAULT DCEL
 */
template <class Traits>
class Pm_default_dcel :
  public Pm_dcel<Pm_vertex_base<typename Traits::Point>,
                 Pm_halfedge_base<typename Traits::X_curve>,
                 Pm_face_base> 
{
public:
  Pm_default_dcel() {}
};

CGAL_END_NAMESPACE

#endif 
