//=======================================================================
// Copyright (c) Aaron Windsor 2007
//
// 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 __FACE_ITERATORS_HPP__
#define __FACE_ITERATORS_HPP__

#include <boost/iterator/iterator_facade.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/graph/graph_traits.hpp>

namespace boost 
{

  //tags for defining traversal properties

  //VisitorType
  struct lead_visitor {};
  struct follow_visitor {};

  //TraversalType
  struct single_side {};
  struct both_sides {};

  //TraversalSubType
  struct first_side {}; //for single_side
  struct second_side {}; //for single_side
  struct alternating {}; //for both_sides

  //Time
  struct current_iteration {};
  struct previous_iteration {};

  // Why TraversalType AND TraversalSubType? TraversalSubType is a function 
  // template parameter passed in to the constructor of the face iterator, 
  // whereas TraversalType is a class template parameter. This lets us decide 
  // at runtime whether to move along the first or second side of a bicomp (by 
  // assigning a face_iterator that has been constructed with TraversalSubType 
  // = first_side or second_side to a face_iterator variable) without any of 
  // the virtual function overhead that comes with implementing this 
  // functionality as a more structured form of type erasure. It also allows 
  // a single face_iterator to be the end iterator of two iterators traversing 
  // both sides of a bicomp.

  //ValueType is either graph_traits<Graph>::vertex_descriptor 
  //or graph_traits<Graph>::edge_descriptor

  
  //forward declaration (defining defaults)
  template <typename Graph,
            typename FaceHandlesMap,
            typename ValueType,
            typename BicompSideToTraverse = single_side,
            typename VisitorType = lead_visitor,
            typename Time = current_iteration
            >
  class face_iterator;
  


  template <typename Graph, bool StoreEdge>
  struct edge_storage
  {};

  template <typename Graph>
  struct edge_storage <Graph, true>
  {
    typename graph_traits<Graph>::edge_descriptor value;
  };




  //specialization for TraversalType = traverse_vertices
  template <typename Graph,
            typename FaceHandlesMap,
            typename ValueType,
            typename TraversalType,
            typename VisitorType,
            typename Time
            >

  class face_iterator
      : public boost::iterator_facade < face_iterator<Graph,
                                                      FaceHandlesMap,
                                                      ValueType,
                                                      TraversalType,
                                                      VisitorType,
                                                      Time
                                                      >,
                                        ValueType,
                                        boost::forward_traversal_tag,
                                        ValueType 
                                      >
  {
  public:

    typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename graph_traits<Graph>::edge_descriptor edge_t;
    typedef face_iterator 
      <Graph,FaceHandlesMap,ValueType,TraversalType,VisitorType,Time> self;
    typedef typename FaceHandlesMap::value_type face_handle_t;

    face_iterator() : 
      m_lead(graph_traits<Graph>::null_vertex()),
      m_follow(graph_traits<Graph>::null_vertex())
    {}

    template <typename TraversalSubType>
    face_iterator(face_handle_t anchor_handle, 
                  FaceHandlesMap face_handles, 
                  TraversalSubType traversal_type):
      m_follow(anchor_handle.get_anchor()), 
      m_face_handles(face_handles)
    {
      set_lead_dispatch(anchor_handle, traversal_type);
    }

    template <typename TraversalSubType>
    face_iterator(vertex_t anchor, 
                  FaceHandlesMap face_handles, 
                  TraversalSubType traversal_type):
      m_follow(anchor), 
      m_face_handles(face_handles)
    {
      set_lead_dispatch(m_face_handles[anchor], traversal_type);
    }

  private:
    
    friend class boost::iterator_core_access;
    



    inline vertex_t get_first_vertex(face_handle_t anchor_handle, 
                                     current_iteration
                                     )
    {
      return anchor_handle.first_vertex();
    }

    inline vertex_t get_second_vertex(face_handle_t anchor_handle, 
                                      current_iteration
                                      )
    {
      return anchor_handle.second_vertex();
    }

    inline vertex_t get_first_vertex(face_handle_t anchor_handle, 
                                     previous_iteration
                                     )
    {
      return anchor_handle.old_first_vertex();
    }

    inline vertex_t get_second_vertex(face_handle_t anchor_handle, 
                                      previous_iteration
                                      )
    {
      return anchor_handle.old_second_vertex();
    }





    inline void set_lead_dispatch(face_handle_t anchor_handle, first_side)
    {
      m_lead = get_first_vertex(anchor_handle, Time());
      set_edge_to_first_dispatch(anchor_handle, ValueType(), Time());
    }
    
    inline void set_lead_dispatch(face_handle_t anchor_handle, second_side)
    {
      m_lead = get_second_vertex(anchor_handle, Time());
      set_edge_to_second_dispatch(anchor_handle, ValueType(), Time());
    }





    inline void set_edge_to_first_dispatch(face_handle_t anchor_handle, 
                                           edge_t, 
                                           current_iteration
                                           )
    {
      m_edge.value = anchor_handle.first_edge();
    }

    inline void set_edge_to_second_dispatch(face_handle_t anchor_handle, 
                                            edge_t, 
                                            current_iteration
                                            )
    {
      m_edge.value = anchor_handle.second_edge();
    }

    inline void set_edge_to_first_dispatch(face_handle_t anchor_handle, 
                                           edge_t, 
                                           previous_iteration
                                           )
    {
      m_edge.value = anchor_handle.old_first_edge();
    }

    inline void set_edge_to_second_dispatch(face_handle_t anchor_handle, 
                                            edge_t, 
                                            previous_iteration
                                            )
    {
      m_edge.value = anchor_handle.old_second_edge();
    }
    
    template<typename T>
    inline void set_edge_to_first_dispatch(face_handle_t, vertex_t, T)
    {}

    template<typename T>
    inline void set_edge_to_second_dispatch(face_handle_t, vertex_t, T)
    {}

    void increment()
    {
      face_handle_t curr_face_handle(m_face_handles[m_lead]);
      vertex_t first = get_first_vertex(curr_face_handle, Time());
      vertex_t second = get_second_vertex(curr_face_handle, Time());
      if (first == m_follow)
        {
          m_follow = m_lead;
          set_edge_to_second_dispatch(curr_face_handle, ValueType(), Time());
          m_lead = second;
        }
      else if (second == m_follow)
        {
          m_follow = m_lead;
          set_edge_to_first_dispatch(curr_face_handle, ValueType(), Time());
          m_lead = first;
        }
      else
        m_lead = m_follow = graph_traits<Graph>::null_vertex();
    }
    
    bool equal(self const& other) const
    {
      return m_lead == other.m_lead && m_follow == other.m_follow;
    }
    
    ValueType dereference() const 
    { 
      return dereference_dispatch(VisitorType(), ValueType());
    }
    
    inline ValueType dereference_dispatch(lead_visitor, vertex_t) const 
    { return m_lead; }
    
    inline ValueType dereference_dispatch(follow_visitor, vertex_t) const 
    { return m_follow; }

    inline ValueType dereference_dispatch(lead_visitor, edge_t) const 
    { return m_edge.value; }

    inline ValueType dereference_dispatch(follow_visitor, edge_t) const 
    { return m_edge.value; }

    vertex_t m_lead;
    vertex_t m_follow;
    edge_storage<Graph, boost::is_same<ValueType, edge_t>::value > m_edge;
    FaceHandlesMap m_face_handles;
  };
  








  template <typename Graph,
            typename FaceHandlesMap,
            typename ValueType,
            typename VisitorType,
            typename Time
            >
  class face_iterator 
    <Graph, FaceHandlesMap, ValueType, both_sides, VisitorType, Time>
    : public boost::iterator_facade< face_iterator<Graph,
                                                   FaceHandlesMap,
                                                   ValueType,
                                                   both_sides,
                                                   VisitorType,
                                                   Time>,
                                     ValueType,
                                     boost::forward_traversal_tag,
                                     ValueType >
  {
  public:

    typedef face_iterator
      <Graph,FaceHandlesMap,ValueType,both_sides,VisitorType,Time> self;
    typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename FaceHandlesMap::value_type face_handle_t;

    face_iterator() {}

    face_iterator(face_handle_t anchor_handle, FaceHandlesMap face_handles):
      first_itr(anchor_handle, face_handles, first_side()),
      second_itr(anchor_handle, face_handles, second_side()),
      first_is_active(true),
      first_increment(true)
    {}

    face_iterator(vertex_t anchor, FaceHandlesMap face_handles):
      first_itr(face_handles[anchor], face_handles, first_side()),
      second_itr(face_handles[anchor], face_handles, second_side()),
      first_is_active(true),
      first_increment(true)
    {}
    
  private:
    
    friend class boost::iterator_core_access;

    typedef face_iterator
      <Graph, FaceHandlesMap, ValueType, single_side, follow_visitor, Time> 
      inner_itr_t;
    
    void increment()
    {
      if (first_increment)
        {
          ++first_itr;
          ++second_itr;
          first_increment = false;
        }
      else if (first_is_active)
        ++first_itr;
      else
        ++second_itr;
      first_is_active = !first_is_active;
    }
    
    bool equal(self const& other) const
    {
      //Want this iterator to be equal to the "end" iterator when at least
      //one of the iterators has reached the root of the current bicomp.
      //This isn't ideal, but it works.

      return (first_itr == other.first_itr || second_itr == other.second_itr);
    }
    
    ValueType dereference() const 
    { 
      return first_is_active ? *first_itr : *second_itr;
    }

    inner_itr_t first_itr;
    inner_itr_t second_itr;
    inner_itr_t face_end;
    bool first_is_active;
    bool first_increment;

  };
  
  
} /* namespace boost */


#endif //__FACE_ITERATORS_HPP__
