//=======================================================================
// Copyright 2007 Aaron Windsor
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef __MAKE_MAXIMAL_PLANAR_HPP__
#define __MAKE_MAXIMAL_PLANAR_HPP__

#include <boost/config.hpp>
#include <boost/tuple/tuple.hpp>   //for tie
#include <boost/graph/biconnected_components.hpp>
#include <boost/property_map/property_map.hpp>
#include <vector>
#include <iterator>
#include <algorithm>

#include <boost/graph/planar_face_traversal.hpp>
#include <boost/graph/planar_detail/add_edge_visitors.hpp>


namespace boost
{


  template <typename Graph, typename VertexIndexMap, typename AddEdgeVisitor>
  struct triangulation_visitor : public planar_face_traversal_visitor
  {

    typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename graph_traits<Graph>::edge_descriptor edge_t;
    typedef typename graph_traits<Graph>::vertices_size_type v_size_t;
    typedef typename graph_traits<Graph>::degree_size_type degree_size_t;
    typedef typename graph_traits<Graph>::edge_iterator edge_iterator_t;
    typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t;
    typedef typename graph_traits<Graph>::adjacency_iterator 
      adjacency_iterator_t;
    typedef typename std::vector<vertex_t> vertex_vector_t;
    typedef typename std::vector<v_size_t> v_size_vector_t;
    typedef typename std::vector<degree_size_t> degree_size_vector_t;
    typedef iterator_property_map
      < typename v_size_vector_t::iterator, VertexIndexMap > 
      vertex_to_v_size_map_t;
    typedef iterator_property_map
      < typename degree_size_vector_t::iterator, VertexIndexMap > 
      vertex_to_degree_size_map_t;
    typedef typename vertex_vector_t::iterator face_iterator;


    triangulation_visitor(Graph& arg_g, 
                          VertexIndexMap arg_vm, 
                          AddEdgeVisitor arg_add_edge_visitor
                          ) : 
      g(arg_g),
      vm(arg_vm),
      add_edge_visitor(arg_add_edge_visitor),
      timestamp(0),
      marked_vector(num_vertices(g), timestamp),
      degree_vector(num_vertices(g), 0),
      marked(marked_vector.begin(), vm),
      degree(degree_vector.begin(), vm)
    {
      vertex_iterator_t vi, vi_end;
      for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
        put(degree, *vi, out_degree(*vi, g));
    }

    template <typename Vertex>
    void next_vertex(Vertex v)
    {
      // Self-loops will appear as consecutive vertices in the list of
      // vertices on a face. We want to skip these.
      if (!vertices_on_face.empty() && 
          (vertices_on_face.back() == v || vertices_on_face.front() == v)
          )
        return;

      vertices_on_face.push_back(v);
    }

    void end_face()
    {
      ++timestamp;

      if (vertices_on_face.size() <= 3)
        {
          // At most three vertices on this face - don't need to triangulate
          vertices_on_face.clear();
          return;
        }
      
      // Find vertex on face of minimum degree
      degree_size_t min_degree = num_vertices(g);
      typename vertex_vector_t::iterator min_degree_vertex_itr;
      face_iterator fi_end = vertices_on_face.end();
      for(face_iterator fi = vertices_on_face.begin(); fi != fi_end; ++fi)
        {
          degree_size_t deg = get(degree,*fi);
          if (deg < min_degree)
            {
              min_degree_vertex_itr = fi;
              min_degree = deg;
            }
        }

      // To simplify some of the manipulations, we'll re-arrange 
      // vertices_on_face so that it still contains the same 
      // (counter-clockwise) order of the vertices on this face, but now the 
      // min_degree_vertex is the first element in vertices_on_face.
      vertex_vector_t temp_vector;
      std::copy(min_degree_vertex_itr, vertices_on_face.end(), 
                std::back_inserter(temp_vector));
      std::copy(vertices_on_face.begin(), min_degree_vertex_itr, 
                std::back_inserter(temp_vector));
      vertices_on_face.swap(temp_vector);

      // Mark all of the min degree vertex's neighbors
      adjacency_iterator_t ai, ai_end;
      for(tie(ai,ai_end) = adjacent_vertices(vertices_on_face.front(),g); 
          ai != ai_end; ++ai
          )
        {
          put(marked, *ai, timestamp);
        }

      typename vertex_vector_t::iterator marked_neighbor 
        = vertices_on_face.end();
     
      // The iterator manipulations on the next two lines are safe because 
      // vertices_on_face.size() > 3 (from the first test in this function)
      fi_end = prior(vertices_on_face.end());
      for(face_iterator fi = boost::next(boost::next(vertices_on_face.begin())); 
          fi != fi_end; ++fi
          )
        {
          if (get(marked, *fi) == timestamp)
            {
              marked_neighbor = fi;
              break;
            }
        }

      if (marked_neighbor == vertices_on_face.end())
        {
          add_edge_range(
                         vertices_on_face[0],
                         boost::next(boost::next(vertices_on_face.begin())),
                         prior(vertices_on_face.end())
                         );
        }
      else
        {
          add_edge_range(
                         vertices_on_face[1],
                         boost::next(marked_neighbor),
                         vertices_on_face.end()
                         );

          add_edge_range(
                         *boost::next(marked_neighbor),
                         boost::next(boost::next(vertices_on_face.begin())),
                         marked_neighbor
                         );
        }

      //reset for the next face
      vertices_on_face.clear();
      
    }

  private:

    
    void add_edge_range(vertex_t anchor, 
                        face_iterator fi, 
                        face_iterator fi_end
                        )
    {
      for (; fi != fi_end; ++fi)
        {
          vertex_t v(*fi);
          add_edge_visitor.visit_vertex_pair(anchor, v, g);
          put(degree, anchor, get(degree, anchor) + 1);
          put(degree, v, get(degree, v) + 1);
        }
    }


    Graph& g;
    VertexIndexMap vm;
    AddEdgeVisitor add_edge_visitor;
    v_size_t timestamp;
    vertex_vector_t vertices_on_face;
    v_size_vector_t marked_vector;
    degree_size_vector_t degree_vector;
    vertex_to_v_size_map_t marked;
    vertex_to_degree_size_map_t degree;
    
  };




  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap,
            typename EdgeIndexMap,
            typename AddEdgeVisitor
            >
  void make_maximal_planar(Graph& g, 
                           PlanarEmbedding embedding,
                           VertexIndexMap vm, 
                           EdgeIndexMap em,
                           AddEdgeVisitor& vis)
  {
    triangulation_visitor<Graph,VertexIndexMap,AddEdgeVisitor> 
      visitor(g, vm, vis);
    planar_face_traversal(g, embedding, visitor, em);
  }




  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap,
            typename EdgeIndexMap
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding,
                           VertexIndexMap vm,
                           EdgeIndexMap em
                           )
  {
    default_add_edge_visitor vis;
    make_maximal_planar(g, embedding, vm, em, vis);
  }




  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding,
                           VertexIndexMap vm
                           )
  {
    make_maximal_planar(g, embedding, vm, get(edge_index,g));
  }




  template <typename Graph,
            typename PlanarEmbedding
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding
                           )
  {
    make_maximal_planar(g, embedding, get(vertex_index,g));
  }


  

} // namespace boost



#endif //__MAKE_MAXIMAL_PLANAR_HPP__
