// Copyright (c) 1997
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel).  All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.1/HalfedgeDS/include/CGAL/HalfedgeDS_decorator.h $
// $Id: include/CGAL/HalfedgeDS_decorator.h 50cfbde3b84 $
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Lutz Kettner  <kettner@mpi-sb.mpg.de>

#ifndef CGAL_HALFEDGEDS_DECORATOR_H
#define CGAL_HALFEDGEDS_DECORATOR_H 1

#include <CGAL/HalfedgeDS_items_decorator.h>
#include <CGAL/HalfedgeDS_const_decorator.h>
#include <CGAL/HalfedgeDS_iterator.h>
#include <CGAL/IO/Verbose_ostream.h>

#include <vector>
#include <map>
#include <list>

namespace CGAL {

template < class p_HDS >
class HalfedgeDS_decorator : public HalfedgeDS_items_decorator<p_HDS> {

// TYPES (inherited from Items_decorator, but have to be repeated)
// ---------------------------------------------------------------
public:

    typedef p_HDS                                 HDS;
    typedef p_HDS                                 HalfedgeDS;
    typedef typename HDS::Traits                  Traits;
    typedef typename HDS::Vertex                  Vertex;
    typedef typename HDS::Halfedge                Halfedge;
    typedef typename HDS::Face                    Face;

    typedef typename HDS::Vertex_handle           Vertex_handle;
    typedef typename HDS::Vertex_const_handle     Vertex_const_handle;
    typedef typename HDS::Vertex_iterator         Vertex_iterator;
    typedef typename HDS::Vertex_const_iterator   Vertex_const_iterator;

    typedef typename HDS::Halfedge_handle         Halfedge_handle;
    typedef typename HDS::Halfedge_const_handle   Halfedge_const_handle;
    typedef typename HDS::Halfedge_iterator       Halfedge_iterator;
    typedef typename HDS::Halfedge_const_iterator Halfedge_const_iterator;

    typedef typename HDS::Face_handle             Face_handle;
    typedef typename HDS::Face_const_handle       Face_const_handle;
    typedef typename HDS::Face_iterator           Face_iterator;
    typedef typename HDS::Face_const_iterator     Face_const_iterator;

    typedef typename HDS::size_type               size_type;
    typedef typename HDS::difference_type         difference_type;
    typedef typename HDS::iterator_category       iterator_category;

// The following types are equal to either `Tag_true' or `Tag_false',
// dependent whether the named feature is supported or not.

    typedef typename HDS::Supports_vertex_halfedge
                                                  Supports_vertex_halfedge;
    typedef typename HDS::Supports_halfedge_prev  Supports_halfedge_prev;
    typedef typename HDS::Supports_halfedge_vertex
                                                  Supports_halfedge_vertex;
    typedef typename HDS::Supports_halfedge_face  Supports_halfedge_face;
    typedef typename HDS::Supports_face_halfedge  Supports_face_halfedge;

    typedef typename HDS::Supports_removal        Supports_removal;


    using HalfedgeDS_items_decorator<p_HDS>::find_prev;
    using HalfedgeDS_items_decorator<p_HDS>::get_prev;
    using HalfedgeDS_items_decorator<p_HDS>::set_prev;
    using HalfedgeDS_items_decorator<p_HDS>::get_face;
    using HalfedgeDS_items_decorator<p_HDS>::set_face;
    using HalfedgeDS_items_decorator<p_HDS>::get_vertex;
    using HalfedgeDS_items_decorator<p_HDS>::set_vertex;
    using HalfedgeDS_items_decorator<p_HDS>::get_vertex_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::set_vertex_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::get_face_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::set_face_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::set_vertex_in_vertex_loop;
    using HalfedgeDS_items_decorator<p_HDS>::set_face_in_face_loop;
    using HalfedgeDS_items_decorator<p_HDS>::insert_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::remove_halfedge;
    using HalfedgeDS_items_decorator<p_HDS>::find_prev_around_vertex;
    using HalfedgeDS_items_decorator<p_HDS>::remove_tip;
    using HalfedgeDS_items_decorator<p_HDS>::close_tip;
    using HalfedgeDS_items_decorator<p_HDS>::insert_tip;

protected:
    typedef typename Vertex::Base                 VBase;
    typedef typename Halfedge::Base               HBase;
    typedef typename Halfedge::Base_base          HBase_base;
    typedef typename Face::Base                   FBase;

// PRIVATE MEMBER VARIABLES
// ----------------------------------
private:
    p_HDS*  hds;

// CREATION
    // ----------------------------------
public:
    // No default constructor, keeps always a reference to a HDS!

    HalfedgeDS_decorator( p_HDS& h) : hds(&h) {}
        // keeps internally a reference to `hds'.

// Creation of New Elements
// ----------------------------------

    Vertex_handle vertices_push_back( const Vertex& v) {
        // appends a copy of v to `hds' if vertices are supported. Returns
        // handle of the new vertex, or `Vertex_handle()' otherwise.
        return vertices_push_back( v, Supports_halfedge_vertex());
    }

    Face_handle faces_push_back( const Face& f) {
        // appends a copy of f to `hds' if faces are supported. Returns
        // handle of the new face, or `Face_handle()' otherwise.
        return faces_push_back( f, Supports_halfedge_face());
    }

private:
    Vertex_handle vertices_push_back( Vertex_const_handle v) {
        // appends a copy of *v to `hds' if vertices are supported. Returns
        // handle of the new vertex, or `Vertex_handle()' otherwise.
        return vertices_push_back( v, Supports_halfedge_vertex());
    }

    Face_handle faces_push_back( Face_const_handle f) {
        // appends a copy of *f to `hds' if faces are supported. Returns
        // handle of the new face, or `Face_handle()' otherwise.
        return faces_push_back( f, Supports_halfedge_face());
    }
public:

// Creation of New Composed Items
// ----------------------------------

    Halfedge_handle create_loop() {
        // returns handle of a halfedge from a newly created loop in `hds'
        // consisting of a single closed edge, one vertex and two faces
        // (if supported respectively).
        Halfedge_handle h = hds->edges_push_back( Halfedge(), Halfedge());
        h->HBase::set_next( h);
        h->opposite()->HBase::set_next( h->opposite());
        set_prev( h, h);
        set_prev( h->opposite(), h->opposite());
        set_vertex( h, vertices_push_back( Vertex()));
        set_vertex( h->opposite(), get_vertex(h));
        set_face( h, faces_push_back( Face()));
        set_face( h->opposite(), faces_push_back( Face()));
        set_face_halfedge( h);
        set_face_halfedge( h->opposite());
        set_vertex_halfedge( h);
        return h;
    }

    Halfedge_handle create_segment() {
        // returns a halfedge from a newly created segment in `hds'
        // consisting of a single open edge, two vertices and one face (if
        // supported respectively).
        Halfedge_handle h = hds->edges_push_back( Halfedge(), Halfedge());
        h->HBase::set_next( h->opposite());
        h->opposite()->HBase::set_next( h);
        set_prev( h, h->opposite());
        set_prev( h->opposite(), h);
        set_vertex( h, vertices_push_back( Vertex()));
        set_vertex( h->opposite(), vertices_push_back( Vertex()));
        set_face( h, faces_push_back( Face()));
        set_face( h->opposite(), get_face(h));
        set_face_halfedge( h);
        set_vertex_halfedge( h);
        set_vertex_halfedge( h->opposite());
        return h;
    }

// Removal of Elements
// ----------------------------------

    void vertices_pop_front() {
        // removes the first vertex if vertices are supported.
        // Precondition: `Supports_removal' == `Tag_true'.
        vertices_pop_front( Supports_halfedge_vertex());
    }
    void vertices_pop_back() {
        // removes the last vertex if vertices are supported.
        vertices_pop_back( Supports_halfedge_vertex());
    }
    void vertices_erase( Vertex_handle v) {
        // removes the vertex v if vertices are supported. Precondition:
        // `Supports_removal' == `Tag_true'.
        vertices_erase( v, Supports_halfedge_vertex());
    }
    void vertices_erase( Vertex_handle first, Vertex_handle last) {
        // removes the range of vertices `[first,last)' if vertices
        // are supported. Precondition: `Supports_removal' ==
        // `Tag_true'.
        vertices_erase( first, last, Supports_halfedge_vertex());
    }

    void faces_pop_front() {
        // removes the first face if faces are supported. Precondition:
        // `Supports_removal' == `Tag_true'.
        faces_pop_front( Supports_halfedge_face());
    }
    void faces_pop_back() {
        // removes the last face if faces are supported.
        faces_pop_back( Supports_halfedge_face());
    }
    void faces_erase( Face_handle f) {
        // removes the face f if faces are supported. Precondition:
        // `Supports_removal' == `Tag_true'.
        faces_erase( f, Supports_halfedge_face());
    }
    void faces_erase( Face_handle first, Face_handle last) {
        // removes the range of faces `[first,last)' if faces
        // are supported. Precondition: `Supports_removal' ==
        // `Tag_true'.
        faces_erase( first, last, Supports_halfedge_face());
    }


// Modifying Functions (Euler Operators)
// -------------------------------------

// The following Euler operations modify consistently the combinatorial
// structure of the halfedge data structure. The geometry remains
// unchanged. Note that well known graph operations are also captured with
// these Euler operators, for example an edge contraction is equal to a
// `join_vertex()' operation, or an edge removal to `join_face()'.
//
// Given a halfedge data structure `hds' and a halfedge handle h four
// special applications of the Euler operators are worth mentioning:
// `split_vertex(h,h)' results in an antenna emanating from the tip of `h';
// `split_vertex(h,h->next()->opposite())' results in an edge split of
// the halfedge `h->next' with a new vertex in-between; `split_face(h,h)'
// results in a loop directly following `h'; and `split_face(h,h->next())'
// results in a bridge parallel to the halfedge `h->next' with a new face
// in-between.

    Halfedge_handle split_face( Halfedge_handle h, Halfedge_handle g) {
        // splits the face incident to `h' and `g' into two faces with a
        // new diagonal between the two vertices denoted by `h' and `g'
        // respectively. The second (new) face obtained from `hds' is a
        // copy of the first face. The new diagonal is returned. The time
        // is proportional to the distance from `h' to `g' around the
        // face.
        Halfedge_handle hnew = hds->edges_push_back( Halfedge(),
                                                     Halfedge());
        Face_handle fnew = faces_push_back( get_face(h));
        insert_tip( hnew, g);
        insert_tip( hnew->opposite(), h);
        set_face( hnew, get_face(h));
        set_face_in_face_loop( hnew->opposite(), fnew);
        set_face_halfedge( hnew);
        set_face_halfedge( hnew->opposite());
        return hnew;
    }

    Halfedge_handle join_face( Halfedge_handle h) {
        // joins the two faces incident to h. The face incident to
        // `h->opposite()' gets removed from `hds'. Both faces might be
        // holes. Returns the predecessor of h around the face. The
        // invariant `join_face( split_face( h, g))' returns h and keeps
        // the data structure unchanged. The time is proportional to the
        // size of the face removed and the time to compute `h->prev()'.
        // Precondition: `Supports_removal' == `Tag_true'.
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        Halfedge_handle hprev = find_prev( h);
        Halfedge_handle gprev = find_prev( h->opposite());
        remove_tip( hprev);
        remove_tip( gprev);
        hds->edges_erase( h);
        if ( get_face( gprev) != Face_handle())
            faces_erase( get_face( gprev));
        h = hprev;
        // 'half' of the halfedges have their correct faces.
        // Here we do the remaining halfedges.
        CGAL_assertion_code( std::size_t termination_count = 0;)
        while ( h != gprev) {
            CGAL_assertion( ++termination_count != 0);
            h = h->next();
            set_face( h, get_face( hprev));
        }
        if ( get_face( hprev) != Face_handle())
            set_face_halfedge(  hprev);
        set_vertex_halfedge( hprev);
        set_vertex_halfedge( gprev);
        return hprev;
    }

    Halfedge_handle split_vertex( Halfedge_handle h, Halfedge_handle g) {
        // splits the vertex incident to `h' and `g' into two vertices and
        // connects them with a new edge. The second (new) vertex obtained
        // from `hds' is a copy of the first vertex. The new edge is
        // returned. The time is proportional to the distance from `h' to
        // `g' around the vertex.
        Halfedge_handle hnew = hds->edges_push_back( Halfedge(),
                                                     Halfedge());
        Vertex_handle vnew = vertices_push_back( get_vertex(h));
        insert_halfedge( hnew, g);
        insert_halfedge( hnew->opposite(), h);
        set_vertex( hnew, get_vertex(h));
        set_vertex_in_vertex_loop( hnew->opposite(), vnew);
        set_vertex_halfedge( hnew);
        set_vertex_halfedge( hnew->opposite());
        return hnew;
    }

    Halfedge_handle join_vertex( Halfedge_handle h) {
        // joins the two vertices incident to h. The vertex denoted by
        // `h->opposite()' gets removed by `hds'. Returns the predecessor
        // of h around the vertex. The invariant `join_vertex(
        // split_vertex( h, g))' returns h and keeps the polyhedron
        // unchanged. The time is proportional to the degree of the vertex
        // removed and the time to compute `h->prev()'. Precondition:
        // `Supports_removal' == `Tag_true'.
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        Halfedge_handle hprev = find_prev( h->opposite());
        Halfedge_handle gprev = find_prev( h);
        remove_halfedge( hprev);
        remove_halfedge( gprev);
        hds->edges_erase( h);
        vertices_erase( get_vertex( gprev));
        // 'half' of the halfedges have their correct vertex.
        // Here we do the remaining halfedges.
        h = hprev;
        CGAL_assertion_code( std::size_t termination_count = 0;)
        while ( h != gprev) {
            CGAL_assertion( ++termination_count != 0);
            h = h->next()->opposite();
            set_vertex( h, get_vertex( hprev));
        }
        set_vertex_halfedge( hprev);
        if ( ! hprev->is_border())
            set_face_halfedge(  hprev);
        if ( ! gprev->is_border())
            set_face_halfedge(  gprev);
        return hprev;
    }

    Halfedge_handle create_center_vertex( Halfedge_handle h) {
        Halfedge_handle hnew = hds->edges_push_back( Halfedge(),
                                                     Halfedge());
        Vertex_handle vnew = vertices_push_back( get_vertex( h));
        close_tip( hnew, vnew);
        insert_tip( hnew->opposite(), h);
        set_face( hnew, get_face( h));
        set_face_halfedge( h);
        Halfedge_handle g = hnew->opposite()->next();
        while ( g->next() != hnew) {
            Halfedge_handle gnew = hds->edges_push_back( Halfedge(),
                                                         Halfedge());
            insert_tip( gnew, hnew);
            insert_tip( gnew->opposite(), g);
            Face_handle fnew = faces_push_back( get_face(hnew));
            set_face( g, fnew);
            set_face( gnew, fnew);
            set_face( gnew->next(), fnew);
            set_face_halfedge( g);
            g = gnew->opposite()->next();
        }
        set_face( hnew->next(), get_face( hnew));
        set_vertex_halfedge( hnew);
        return hnew;
    }

    Halfedge_handle erase_center_vertex( Halfedge_handle h) {
        // h points to the vertex that gets removed
        Halfedge_handle g    = h->next()->opposite();
        Halfedge_handle hret = find_prev( h);
        while ( g != h) {
            Halfedge_handle gprev = find_prev( g);
            set_vertex_halfedge( gprev);
            remove_tip( gprev);
            if ( get_face( g) != Face_handle())
                faces_erase( get_face( g));
            Halfedge_handle gnext = g->next()->opposite();
            hds->edges_erase( g);
            g = gnext;
        }
        set_vertex_halfedge( hret);
        remove_tip( hret);
        vertices_erase( get_vertex( h));
        hds->edges_erase( h);
        set_face_in_face_loop( hret, get_face( hret));
        set_face_halfedge(hret);
        return hret;
    }

    Halfedge_handle split_loop( Halfedge_handle h,
                                Halfedge_handle i,
                                Halfedge_handle j) {
        // cuts the halfedge data structure into two parts along the cycle
        // (h,i,j). Three new vertices (one copy for each vertex in the
        // cycle) and three new halfedges (one copy for each halfedge in
        // the cycle), and two new triangles are created. h,i,j will be
        // incident to the first new triangle. The return value will be
        // the halfedge incident to the second new triangle which is the
        // copy of `h-opposite()'. Precondition: h,i,j denote distinct,
        // consecutive vertices of the halfedge data structure and form a
        // cycle: i.e. `h->vertex() == i->opposite()->vertex()', ...,
        // `j->vertex() == h->opposite()->vertex()'.
        CGAL_precondition( h != i);
        CGAL_precondition( h != j);
        CGAL_precondition( i != j);
        CGAL_precondition( get_vertex(h) == get_vertex(i->opposite()));
        CGAL_precondition( get_vertex(i) == get_vertex(j->opposite()));
        CGAL_precondition( get_vertex(j) == get_vertex(h->opposite()));
        // Create a copy of the triangle.
        Halfedge_handle hnew = hds->edges_push_back( *h);
        Halfedge_handle inew = hds->edges_push_back( *i);
        Halfedge_handle jnew = hds->edges_push_back( *j);
        close_tip( hnew, vertices_push_back( get_vertex( h)));
        close_tip( inew, vertices_push_back( get_vertex( i)));
        close_tip( jnew, vertices_push_back( get_vertex( j)));
        insert_tip( inew->opposite(), hnew);
        insert_tip( jnew->opposite(), inew);
        insert_tip( hnew->opposite(), jnew);
        // Make the new incidences with the old structure.
        CGAL_assertion_code( std::size_t termination_count = 0;)
        if ( h->next() != i) {
            Halfedge_handle g = h->next();
            h->HBase::set_next( i);
            set_prev( i, h);
            hnew->HBase::set_next( g);
            set_prev( g, hnew);
            g = g->opposite();
            while ( g->next() != i) {
                CGAL_assertion( ++termination_count != 0);
                set_vertex( g, get_vertex( hnew));
                g = g->next()->opposite();
            }
            set_vertex( g, get_vertex( hnew));
            g->HBase::set_next( inew);
            set_prev( inew, g);
        }
        if ( i->next() != j) {
            Halfedge_handle g = i->next();
            i->HBase::set_next( j);
            set_prev( j, i);
            inew->HBase::set_next( g);
            set_prev( g, inew);
            g = g->opposite();
            while ( g->next() != j) {
                CGAL_assertion( ++termination_count != 0);
                set_vertex( g, get_vertex( inew));
                g = g->next()->opposite();
            }
            set_vertex( g, get_vertex( inew));
            g->HBase::set_next( jnew);
            set_prev( jnew, g);
        }
        if ( j->next() != h) {
            Halfedge_handle g = j->next();
            j->HBase::set_next( h);
            set_prev( h, j);
            jnew->HBase::set_next( g);
            set_prev( g, jnew);
            g = g->opposite();
            while ( g->next() != h) {
                CGAL_assertion( ++termination_count != 0);
                set_vertex( g, get_vertex( jnew));
                g = g->next()->opposite();
            }
            set_vertex( g, get_vertex( jnew));
            g->HBase::set_next( hnew);
            set_prev( hnew, g);
        }
        // Fill the holes with two new faces.
        Face_handle f = faces_push_back( Face());
        set_face( h, f);
        set_face( i, f);
        set_face( j, f);
        set_face_halfedge( h);
        f = faces_push_back( Face());
        set_face( hnew->opposite(), f);
        set_face( inew->opposite(), f);
        set_face( jnew->opposite(), f);
        set_face_halfedge( hnew->opposite());
        // Take care of maybe changed halfedge pointers.
        set_face_halfedge( hnew);
        set_face_halfedge( inew);
        set_face_halfedge( jnew);
        set_vertex_halfedge( hnew);
        set_vertex_halfedge( inew);
        set_vertex_halfedge( jnew);
        return hnew->opposite();
    }

    Halfedge_handle join_loop( Halfedge_handle h, Halfedge_handle g) {
        // glues the boundary of the two faces denoted by h and g together
        // and returns h. Both faces and the vertices along the face
        // denoted by g gets removed. Both faces may be holes. The
        // invariant `join_loop( h, split_loop( h, i, j))' returns h and
        // keeps the data structure unchanged. Precondition:
        // `Supports_removal' == `Tag_true'. The faces denoted by h
        // and g are different and have equal degree (i.e. number of
        // edges).
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        CGAL_precondition(    get_face(h) == Face_handle()
                    || get_face(h) != get_face(g));
        if ( get_face( h) != Face_handle())
            faces_erase( get_face(h));
        if ( get_face( g) != Face_handle())
            faces_erase( get_face(g));
        Halfedge_handle hi = h;
        Halfedge_handle gi = g;
        CGAL_assertion_code( std::size_t termination_count = 0;)
        do {
            CGAL_assertion( ++termination_count != 0);
            Halfedge_handle hii = hi;
            Halfedge_handle gii = gi;
            hi = hi->next();
            // gi = find_prev(gi); // Replaced by search around vertex.
            set_face( hii, get_face( gii->opposite()));
            set_face_halfedge( hii);
            vertices_erase( get_vertex( gii->opposite()));
            if ( gii->opposite()->next()->opposite()->next() == gii) {
                gi = gii->opposite()->next()->opposite();
            } else {
                hii->HBase::set_next( gii->opposite()->next());
                set_prev( hii->next(), hii);
                gii = gii->opposite()->next()->opposite();
                set_vertex( gii, get_vertex(hii));
                while ( gii->next()->opposite()->next() != gi) {
                    CGAL_assertion( ++termination_count != 0);
                    gii = gii->next()->opposite();
                    set_vertex( gii, get_vertex(hii));
                }
                gi = gii->next()->opposite();
                gii->HBase::set_next( hi);
                set_prev( gii->next(), gii);
            }
        } while ( hi != h);
        CGAL_assertion( gi == g);
        do {
            Halfedge_handle gii = gi;
            gi = gi->next();
            hds->edges_erase( gii);
        } while ( gi != g);
        return h;
    }

  protected:
    // supports face or not.
    void make_hole( Halfedge_handle, Tag_false) {}
    void fill_hole( Halfedge_handle, Tag_false) {}
    void fill_hole( Halfedge_handle, const Face&, Tag_false) {}

    void make_hole( Halfedge_handle h, Tag_true) {
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        CGAL_precondition( h != Halfedge_handle());
        CGAL_precondition( ! h->is_border());
        hds->faces_erase( h->face());
        set_face_in_face_loop( h, Face_handle());
    }

    void fill_hole( Halfedge_handle h, Tag_true) {
        CGAL_precondition( h != Halfedge_handle());
        CGAL_precondition( h->is_border());
        Face_handle f = faces_push_back( Face());
        set_face_in_face_loop( h, f);
        set_face_halfedge( h);
    }

    void fill_hole( Halfedge_handle h, const Face& f, Tag_true) {
        CGAL_precondition( h != Halfedge_handle());
        CGAL_precondition( h->is_border());
        Face_handle fh = faces_push_back( f);
        set_face_in_face_loop( h, fh);
        set_face_halfedge( h);
    }

  public:

    Halfedge_handle make_hole( Halfedge_handle h) {
        // removes the face incident to `h' from `hds' and creates a hole.
        // Precondition: `h != Halfedge_handle()' and `!(h->is_border())'.
        // If faces are supported, `Supports_removal' == `Tag_true'.
        make_hole( h, Supports_halfedge_face());
        return h;
    }

    Halfedge_handle fill_hole( Halfedge_handle h) {
        // fills the hole incident to `h' with a new face from `hds'.
        // Precondition: `h != Halfedge_handle()' and `h->is_border()'.
        fill_hole( h, Supports_halfedge_face());
        return h;
    }

    Halfedge_handle fill_hole( Halfedge_handle h, const Face& f) {
        // fills the hole incident to `h' with a copy of face f.
        // Precondition: `h != Halfedge_handle()' and `h->is_border()'.
        fill_hole( h, f, Supports_halfedge_face());
        return h;
    }

    Halfedge_handle add_face_to_border( Halfedge_handle h,
                                        Halfedge_handle g,
                                        const Face& f) {
        // extends the surface with a copy of face f into the hole
        // incident to h and g. It creates a new edge connecting the tip
        // of g with the tip of h and fills this separated part of the
        // hole with a copy of face f, such that the new face is incident
        // to g. Returns the new halfedge that is incident to the new
        // face. Precondition: `h != Halfedge_handle()', `g !=
        // Halfedge_handle()', `h->is_border()', `g->is_border()' and g
        // can be reached along the hole starting with h.
        CGAL_precondition( h != Halfedge_handle());
        CGAL_precondition( g != Halfedge_handle());
        CGAL_precondition( h->is_border());
        CGAL_precondition( g->is_border());
        Halfedge_handle hh = hds->edges_push_back( Halfedge(), Halfedge());
        insert_tip( hh, h);
        insert_tip( hh->opposite(), g);
        fill_hole( g, f);
        return hh;
    }

    Halfedge_handle add_face_to_border( Halfedge_handle h,
                                        Halfedge_handle g) {
        // extends the surface with a new face from `hds' into the hole
        // incident to h and g. It creates a new edge connecting the tip
        // of g with the tip of h and fills this separated part of the
        // hole with a new face, such that the new face is incident to g.
        // Returns the new halfedge that is incident to the new face.
        // Precondition: `h != Halfedge_handle()', `g != Halfedge_handle(
        // )', `h->is_border()', `g->is_border()' and g can be reached
        // along the hole starting with h.
        return add_face_to_border( h, g, Face());
    }

// Erasing
// ----------------------------------
  protected:
    // supports face or not.
    void erase_face( Halfedge_handle,   Tag_false) {}
    void erase_face( Halfedge_handle h, Tag_true)  {
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        CGAL_precondition( h != Halfedge_handle());
        CGAL_precondition( ! h->is_border());
        hds->faces_erase( h->face());
        CGAL_assertion_code( std::size_t termination_count = 0;)
        Halfedge_handle end = h;
        do {
            CGAL_assertion( ++termination_count != 0);
            set_face( h, Face_handle());
            Halfedge_handle g = h->next();
            bool h_tag = h->opposite()->is_border();
            bool g_tag = g->opposite()->is_border();
            if ( h_tag && g_tag && g->opposite()->next() == h->opposite()){
                vertices_erase( get_vertex(h));
                if ( h != end)
                    hds->edges_erase( h);
            } else {
                if ( g_tag) {
                    set_vertex_halfedge(g->opposite()->next()->opposite());
                    remove_tip(h);
                }
                if ( h_tag) {
                    set_vertex_halfedge(h->next()->opposite());
                    remove_tip( find_prev_around_vertex( h->opposite()));
                    if ( h != end)
                        hds->edges_erase(h);
                }
            }
            h = g;
        } while ( h != end);
        if ( h->opposite()->is_border())
            hds->edges_erase( h);
    }

  public:
    void erase_face( Halfedge_handle h) {
        // removes the face incident to `h' from `hds' and changes all
        // halfedges incident to the face into border edges or removes
        // them from the halfedge data structure if they were already
        // border edges. See `make_hole(h)' for a more specialized
        // variant. Precondition: `h != Halfedge_handle()'. If faces are
        // supported, `Supports_removal' == `Tag_true'.
        erase_face( h, Supports_halfedge_face());
    }

  protected:                               // Supports_halfedge_vertices
      void erase_connected_component_vertex( Halfedge_handle  ,Tag_false){}
      void erase_connected_component_vertex( Halfedge_handle h, Tag_true) {
          // Erases the vertex incident to h and sets all references
          // from halfedges around this vertex to Halfedge_handle(),
          // if the incident vertex handle is not already equal to
          // Halfedge_handle(). It is used to erase vertices as soon
          // as an vertex is encountered in the graph traversal. At this
          // point of the graph traversal the halfedge cycle around the
          // vertex is still closed. Lateron it will be broken.
          if ( h->vertex() != Vertex_handle()) {
              hds->vertices_erase( h->vertex());
              set_vertex_in_vertex_loop( h, Vertex_handle());
          }
      }
      void erase_connected_component_vertex( Halfedge_handle h) {
          erase_connected_component_vertex( h, Supports_halfedge_vertex());
      }

      void erase_connected_component_face_cycle( Halfedge_handle h,
                  std::vector<Halfedge_handle>& stack) {
          // Delete incident face and set all incidences to Face_handle().
          if ( get_face(h) != Face_handle()) {
              faces_erase( get_face(h));
              set_face_in_face_loop( h, Face_handle());
          }
          // Cycle around face, delete incident vertices, push new
          // edges on the stack and mark edges as visited.
          erase_connected_component_vertex( h);
          Halfedge_handle g = h->next();
          h->HBase::set_next( Halfedge_handle());
          while (g != h) {
              erase_connected_component_vertex( g);
              if ( g->opposite()->next() != Halfedge_handle())
                  stack.push_back( g->opposite());
              Halfedge_handle gg = g->next();
              g->HBase::set_next( Halfedge_handle());
              g = gg;
          }
      }

  public:
    void erase_connected_component( Halfedge_handle h) {
        // removes the vertices, halfedges, and facets that belong to the
        // connected component of h. Precondition: `Supports_removal'
        // == `Tag_true'. For all halfedges g in the connected
        // component `g.next() != Halfedge_handle()'.
        Assert_compile_time_tag( Tag_true(), Supports_removal());
        typedef std::vector<Halfedge_handle> HVector;
        HVector stack;
        // Algorithm: The next() pointer is used as visited tag
        //     for a graph search. If the next pointer of an halfedge
        //     or its opposite halfedge is set to Halfedge_handle(),
        //     this edge has already been visited and must not be put
        //     on the stack again.
        // Initializing: Cycle through the face-cycle of h and put
        //     all opposite halfedges on the stack. Put h->opposite()
        //     on the stack. Note that even if the face cycle of h looks
        //     ugly ( e.g. h->opposite() is also in the cycle), neither
        //     h nor h->opposite() will be put on the stack. If
        //     h->opposite() is in the cycle, when h will be popped from
        //     the stack it will be immediately deleted.
        // Loop invariant: For each edge h on the stack h->opposite()->
        //     next() == Halfedge_handle().
        // Looping: For each edge h on the stack, if h->next() is
        //     not already equal to Halfedge_handle(), cycle through
        //     the face-cycle of h and put all opposite halfedges on
        //     the stack. Delete h.
        // Where: Cycle through a face means: If h->face() !=
        //     Halfedge_handle() delete h->face() and set all face
        //     handles to Halfedge_handle(). Loop through the
        //     halfedges g around the face, call
        //     erase_connected_component_vertex for each g, push
        //     g->opposite() on the stack if g->opposite()->next()
        //     is not already Halfedge_handle(). This implies that
        //     h->opposite() is not put on the stack again.
        erase_connected_component_face_cycle( h, stack);
        stack.push_back( h->opposite());
        while ( ! stack.empty()) {
            h = stack.back();
            stack.pop_back();
            CGAL_assertion( h->opposite()->next() == Halfedge_handle());
            if ( h->next() != Halfedge_handle())
                erase_connected_component_face_cycle( h, stack);
            hds->edges_erase( h);
        }
    }

    /// Erases the small connected components and the isolated vertices.
    ///
    /// @commentheading Preconditions:
    /// supports vertices, halfedges, and removal operation.
    ///
    /// @commentheading Template Parameters:
    /// @param nb_components_to_keep the number of large connected components to keep.
    ///
    /// @return the number of connected components erased (ignoring isolated vertices).
    unsigned int keep_largest_connected_components(unsigned int nb_components_to_keep)
    {
        Assert_compile_time_tag(Supports_removal(), Tag_true());
        Assert_compile_time_tag(Supports_vertex_halfedge(), Tag_true());
        Assert_compile_time_tag(Supports_halfedge_vertex(), Tag_true());

        unsigned int nb_erased_components = 0;

        // Gets list of connected components, ordered by size (i.e. number of vertices)
        std::vector<Vertex_handle> components;
        get_connected_components(std::back_inserter(components));

        // Erases all connected components but the largest
        while (components.size() > nb_components_to_keep)
        {
          Vertex_handle vertex = *(components.begin());

          // Removes component from list
          components.erase(components.begin());

          if (vertex->halfedge() != nullptr) // if not isolated vertex
          {
            erase_connected_component(vertex->halfedge());
            nb_erased_components++;
          }
          else // if isolated vertex
          {
            vertices_erase(vertex);
          }
        }

        return nb_erased_components;
    }

// Implementing These Functions.
// ====================================================
// Creation of New Elements
// ----------------------------------
private:

    Vertex_handle vertices_push_back( const Vertex&  , Tag_false) {
        return Vertex_handle();
    }
    Vertex_handle vertices_push_back( const Vertex& v, Tag_true) {
        return hds->vertices_push_back(v);
    }

    Vertex_handle vertices_push_back( Vertex_const_handle  , Tag_false) {
        return Vertex_handle();
    }
    Vertex_handle vertices_push_back( Vertex_const_handle v, Tag_true) {
        return hds->vertices_push_back(*v);
    }

    Face_handle faces_push_back( const Face&  , Tag_false) {
        return Face_handle();
    }
    Face_handle faces_push_back( const Face& f, Tag_true) {
        return hds->faces_push_back(f);
    }

    Face_handle faces_push_back( Face_const_handle  , Tag_false) {
        return Face_handle();
    }
    Face_handle faces_push_back( Face_const_handle f, Tag_true) {
        return hds->faces_push_back(*f);
    }


// Removal of Elements
// ----------------------------------

    void vertices_erase( Vertex_handle  , Tag_false) {}
    void vertices_erase( Vertex_handle v, Tag_true) {
        hds->vertices_erase( v);
    }

    void vertices_erase( Vertex_handle  , Vertex_handle  , Tag_false) {}
    void vertices_erase( Vertex_handle v1, Vertex_handle v2, Tag_true) {
        hds->vertices_erase( v1, v2);
    }

    void vertices_pop_front( Tag_false) {}
    void vertices_pop_front( Tag_true) {
        hds->vertices_pop_front();
    }

    void vertices_pop_back( Tag_false) {}
    void vertices_pop_back( Tag_true) {
        hds->vertices_pop_back();
    }

    void faces_erase( Face_handle  , Tag_false) {}
    void faces_erase( Face_handle f, Tag_true) {
        hds->faces_erase( f);
    }

    void faces_erase( Face_handle  , Face_handle  , Tag_false) {}
    void faces_erase( Face_handle f1, Face_handle f2, Tag_true) {
        hds->faces_erase( f1, f2);
    }

    void faces_pop_front( Tag_false) {}
    void faces_pop_front( Tag_true) {
        hds->faces_pop_front();
    }

    void faces_pop_back( Tag_false) {}
    void faces_pop_back( Tag_true) {
        hds->faces_pop_back();
    }

    /// Helper type for keep_largest_connected_components():
    ///
    /// Possible values of a vertex tag.
    enum { tag_free, tag_done };

    /// Helper method for keep_largest_connected_components():
    ///
    /// Gets any vertex with tag == tag_free.
    ///
    /// @return a list of pairs (component's size (number of vertices), a vertex of the component),
    /// ordered by size.
    Vertex_handle get_any_free_vertex(
      /*const*/ std::map<Vertex*, int>& tags) ///< container holding the tag of all vertices
    {
        for (Vertex_iterator it = hds->vertices_begin(); it != hds->vertices_end(); it++)
        {
            if (tags[&*it] == tag_free)
                return it;
        }

        return nullptr;
    }

    /// Helper method for keep_largest_connected_components():
    ///
    /// Tag a "free" connected component as "done".
    ///
    /// @return the size (number of vertices) of the component.
    unsigned int tag_component(
      Vertex_handle pSeedVertex, ///< one vertex of the connected component
      std::map<Vertex*, int>& tags) ///< container holding the tag of all vertices
    {
        // Circulator category.
        typedef typename Halfedge::Supports_halfedge_prev  Supports_prev;
        typedef HalfedgeDS_circulator_traits<Supports_prev> Ctr;
        typedef typename Ctr::iterator_category circulator_category;

        // Circulator around a vertex
        typedef I_HalfedgeDS_vertex_circ< Halfedge_handle, circulator_category>
                                            Halfedge_around_vertex_circulator;

        unsigned int number_of_vertices = 0; // size (number of vertices) of the component

        std::list<Vertex_handle> vertices;
        vertices.push_front(pSeedVertex);
        while (!vertices.empty())
        {
            Vertex_handle pVertex = vertices.front();
            vertices.pop_front();

            // Skip vertex if already done
            if (tags[&*pVertex] == tag_done)
                continue;

            // Mark vertex done
            tags[&*pVertex] = tag_done;
            number_of_vertices++;

            // Add vertex's "free" neighbors to the list
            Halfedge_around_vertex_circulator neighbor_cir(pVertex->halfedge()),
                                              neighbor_end = neighbor_cir;
            CGAL_For_all(neighbor_cir,neighbor_end)
            {
                Vertex_handle neighbor = neighbor_cir->opposite()->vertex();
                if (tags[&*neighbor] == tag_free)
                    vertices.push_front(neighbor);
            }
        }

        return number_of_vertices;
    }

    /// Helper method for keep_largest_connected_components():
    ///
    /// Computes the list of all connected components of the polyhedron.
    /// Returns it as a list of components ordered by size.
    ///
    /// @commentheading Template Parameters:
    /// @param OutputIterator value_type must be Vertex_handle.
    template<typename OutputIterator>
    void get_connected_components(
        OutputIterator output) ///< output iterator over vertex handles
    {
        // Implementation note:
        // We tag vertices instead of halfedges to save a factor 6.
        // The drawback is that we require the Polyhedron_3<Traits> to support vertices.
        // TODO: replace std::map by a property map to tag vertices.
        Assert_compile_time_tag(Supports_halfedge_vertex(), Tag_true());
        std::map<Vertex*, int> tags;

        // list of all connected components of a polyhedron, ordered by size.
        std::multimap<unsigned int, Vertex_handle> components;

        // Tag all mesh vertices as "free".
        for (Vertex_iterator it = hds->vertices_begin(); it != hds->vertices_end(); it++)
        {
            tags[&*it] = tag_free;
        }

        // Record each component
        Vertex_handle seed_vertex = nullptr;
        while((seed_vertex = get_any_free_vertex(tags)) != nullptr)
        {
            // Tag it as "done" and compute its size (number of vertices)
            unsigned int number_of_vertices = tag_component(seed_vertex, tags);

            // Add component to ordered list
            components.insert(std::make_pair(number_of_vertices, seed_vertex));
        }

        // Copy ordered list to output iterator
        typename std::multimap<unsigned int, Vertex_handle>::iterator src;
        for (src = components.begin(); src != components.end(); ++src)
          *output++ = src->second;
    }

// Others
// ----------------------------------
public:

    bool normalized_border_is_valid( bool verb = false) const {
        // returns `true' if the border halfedges are in normalized
        // representation, which is when enumerating all halfedges with
        // the halfedge iterator the following holds: The non-border edges
        // precede the border edges. For border edges, the second halfedge
        // is a border halfedge. (The first halfedge may or may not be a
        // border halfedge.) The halfedge iterator `border_halfedges_begin
        // ()' denotes the first border edge. If `verbose' is `true',
        // statistics are written to `cerr'.
        HalfedgeDS_const_decorator<p_HDS> decorator(*hds);
        return decorator.normalized_border_is_valid( verb);
    }
    bool is_valid( bool verb = false, int level = 0) const {
        // returns `true' if the halfedge data structure `hds' is valid
        // with respect to the `level' value as defined in the reference
        // manual. If `verbose' is `true', statistics are written to
        // `cerr'.
        HalfedgeDS_const_decorator<p_HDS> decorator(*hds);
        return decorator.is_valid( verb, level);
    }
    void reorient_face( Halfedge_handle first);
                  // Supports: halfedge pointer in faces.
    void inside_out( Tag_false);
    void inside_out( Tag_true);

    void inside_out() {
        inside_out( Supports_face_halfedge());
    }
};

template < class p_HDS >  CGAL_MEDIUM_INLINE
void
HalfedgeDS_decorator<p_HDS>::
reorient_face( Halfedge_handle first) {
    if ( first == Halfedge_handle())
        return;
    Halfedge_handle last  = first;
    Halfedge_handle prev  = first;
    Halfedge_handle start = first;
    first = first->next();
    Vertex_handle  new_v = get_vertex( start);
    while (first != last) {
        Vertex_handle  tmp_v = get_vertex( first);
        set_vertex( first, new_v);
        set_vertex_halfedge( first);
        new_v = tmp_v;
        Halfedge_handle next = first->next();
        first->HBase::set_next( prev);
        set_prev( first, next);
        prev  = first;
        first = next;
    }
    set_vertex( start, new_v);
    set_vertex_halfedge( start);
    Halfedge_handle next = start->next();
    start->HBase::set_next( prev);
    set_prev( start, next);
}

template < class p_HDS >
void                            // Supports: halfedge pointer in faces.
HalfedgeDS_decorator<p_HDS>::
inside_out( Tag_false) {
    Halfedge_iterator begin = hds->halfedges_begin();
    Halfedge_iterator end   = hds->halfedges_end();
    for( ; begin != end; ++begin) {
        // Define the halfedge with the `smallest' pointer
        // value as the one that is used to reorient the face.
        Halfedge_handle h = begin;
        bool is_min = true;
        Halfedge_handle c = h;
        Halfedge_handle d = c;
        do {
            CGAL_assertion( c != Halfedge_handle());
            if ( &*h > &*c)
                is_min = false;
            c = c->next();
        } while ( c != d && is_min);
        if ( is_min)
            reorient_face( h);
    }
}

template < class p_HDS >
void                            // Supports: halfedge pointer in faces.
HalfedgeDS_decorator<p_HDS>::
inside_out( Tag_true) {
    Face_iterator begin = hds->faces_begin();
    Face_iterator end   = hds->faces_end();
    for( ; begin != end; ++begin)
        reorient_face( begin->halfedge());
    // Note: A border edge is now parallel to its opposite edge.
    // We scan all border edges for this property. If it holds, we
    // reorient the associated hole and search again until no border
    // edge with that property exists any longer. Then, all holes are
    // reoriented.
    Halfedge_iterator first = hds->halfedges_begin();
    Halfedge_iterator last  = hds->halfedges_end();
    for( ; first != last; ++first) {
        if ( first->is_border() &&
             first->vertex() == first->opposite()->vertex())
            reorient_face( first);
    }
}

} //namespace CGAL

#endif // CGAL_HALFEDGEDS_DECORATOR_H //
// EOF //

