//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// 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 BOOST_GRAPH_EDGE_LIST_HPP
#define BOOST_GRAPH_EDGE_LIST_HPP

#include <iterator>
#include <boost/config.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/range/irange.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>

namespace boost {

  //
  // The edge_list class is an EdgeListGraph module that is constructed
  // from a pair of iterators whose value type is a pair of vertex
  // descriptors.
  //
  // For example:
  //
  //  typedef std::pair<int,int> E;
  //  list<E> elist;
  //  ...
  //  typedef edge_list<list<E>::iterator> Graph;
  //  Graph g(elist.begin(), elist.end());
  //
  // If the iterators are random access, then Graph::edge_descriptor
  // is of Integral type, otherwise it is a struct, though it is
  // convertible to an Integral type.
  //

  struct edge_list_tag { };

  // The implementation class for edge_list.
  template <class G, class EdgeIter, class T, class D>
  class edge_list_impl
  {
  public:
    typedef D edge_id;
    typedef T Vpair;
    typedef typename Vpair::first_type V;
    typedef V vertex_descriptor;
    typedef edge_list_tag graph_tag;
    typedef void edge_property_type;

    struct edge_descriptor
    {
      edge_descriptor() { }
      edge_descriptor(EdgeIter p, edge_id id) : _ptr(p), _id(id) { }
      operator edge_id() { return _id; }
      EdgeIter _ptr;
      edge_id _id;
    };
    typedef edge_descriptor E;

    struct edge_iterator
    {
      typedef edge_iterator self;
      typedef E value_type;
      typedef E& reference;
      typedef E* pointer;
      typedef std::ptrdiff_t difference_type;
      typedef std::input_iterator_tag iterator_category;
      edge_iterator() { }
      edge_iterator(EdgeIter iter) : _iter(iter), _i(0) { }
      E operator*() { return E(_iter, _i); }
      self& operator++() { ++_iter; ++_i; return *this; }
      self operator++(int) { self t = *this; ++(*this); return t; }
      bool operator==(const self& x) { return _iter == x._iter; }
      bool operator!=(const self& x) { return _iter != x._iter; }
      EdgeIter _iter;
      edge_id _i;
    };
    typedef void out_edge_iterator;
    typedef void in_edge_iterator;
    typedef void adjacency_iterator;
    typedef void vertex_iterator;
  };

  template <class G, class EI, class T, class D>
  std::pair<typename edge_list_impl<G,EI,T,D>::edge_iterator,
            typename edge_list_impl<G,EI,T,D>::edge_iterator>
  edges(const edge_list_impl<G,EI,T,D>& g_) {
    const G& g = static_cast<const G&>(g_);
    typedef typename edge_list_impl<G,EI,T,D>::edge_iterator edge_iterator;
    return std::make_pair(edge_iterator(g._first), edge_iterator(g._last));
  }
  template <class G, class EI, class T, class D>
  typename edge_list_impl<G,EI,T,D>::vertex_descriptor
  source(typename edge_list_impl<G,EI,T,D>::edge_descriptor e,
         const edge_list_impl<G,EI,T,D>&) {
    return (*e._ptr).first;
  }
  template <class G, class EI, class T, class D>
  typename edge_list_impl<G,EI,T,D>::vertex_descriptor
  target(typename edge_list_impl<G,EI,T,D>::edge_descriptor e,
           const edge_list_impl<G,EI,T,D>&) {
    return (*e._ptr).second;
  }

  template <class D, class E>
  class el_edge_property_map
    : public put_get_helper<D, el_edge_property_map<D,E> >{
  public:
    typedef E key_type;
    typedef D value_type;
    typedef D reference;
    typedef readable_property_map_tag category;

    value_type operator[](key_type e) const {
      return e._i;
    }
  };
  struct edge_list_edge_property_selector {
    template <class Graph, class Property, class Tag>
    struct bind_ {
      typedef el_edge_property_map<typename Graph::edge_id,
          typename Graph::edge_descriptor> type;
      typedef type const_type;
    };
  };
  template <>
  struct edge_property_selector<edge_list_tag> {
    typedef edge_list_edge_property_selector type;
  };

  template <class G, class EI, class T, class D>
  typename property_map< edge_list_impl<G,EI,T,D>, edge_index_t>::type
  get(edge_index_t, const edge_list_impl<G,EI,T,D>&) {
    typedef typename property_map< edge_list_impl<G,EI,T,D>,
      edge_index_t>::type EdgeIndexMap;
    return EdgeIndexMap();
  }

  template <class G, class EI, class T, class D>
  inline D
  get(edge_index_t, const edge_list_impl<G,EI,T,D>&,
      typename edge_list_impl<G,EI,T,D>::edge_descriptor e) {
    return e._i;
  }

  // A specialized implementation for when the iterators are random access.

  struct edge_list_ra_tag { };

  template <class G, class EdgeIter, class T, class D>
  class edge_list_impl_ra
  {
  public:
    typedef D edge_id;
    typedef T Vpair;
    typedef typename Vpair::first_type V;
    typedef edge_list_ra_tag graph_tag;
    typedef void edge_property_type;

    typedef edge_id edge_descriptor;
    typedef V vertex_descriptor;
    typedef typename boost::integer_range<edge_id>::iterator edge_iterator;
    typedef void out_edge_iterator;
    typedef void in_edge_iterator;
    typedef void adjacency_iterator;
    typedef void vertex_iterator;
  };

  template <class G, class EI, class T, class D>
  std::pair<typename edge_list_impl_ra<G,EI,T,D>::edge_iterator,
            typename edge_list_impl_ra<G,EI,T,D>::edge_iterator>
  edges(const edge_list_impl_ra<G,EI,T,D>& g_)
  {
    const G& g = static_cast<const G&>(g_);
    typedef typename edge_list_impl_ra<G,EI,T,D>::edge_iterator edge_iterator;
    return std::make_pair(edge_iterator(0), edge_iterator(g._last - g._first));
  }
  template <class G, class EI, class T, class D>
  typename edge_list_impl_ra<G,EI,T,D>::vertex_descriptor
  source(typename edge_list_impl_ra<G,EI,T,D>::edge_descriptor e,
         const edge_list_impl_ra<G,EI,T,D>& g_)
  {
    const G& g = static_cast<const G&>(g_);
    return g._first[e].first;
  }
  template <class G, class EI, class T, class D>
  typename edge_list_impl_ra<G,EI,T,D>::vertex_descriptor
  target(typename edge_list_impl_ra<G,EI,T,D>::edge_descriptor  e,
         const edge_list_impl_ra<G,EI,T,D>& g_)
  {
    const G& g = static_cast<const G&>(g_);
    return g._first[e].second;
  }
  template <class E>
  class el_ra_edge_property_map
    : public put_get_helper<E, el_ra_edge_property_map<E> >{
  public:
    typedef E key_type;
    typedef E value_type;
    typedef E reference;
    typedef readable_property_map_tag category;

    value_type operator[](key_type e) const {
      return e;
    }
  };
  struct edge_list_ra_edge_property_selector {
    template <class Graph, class Property, class Tag>
    struct bind_ {
      typedef el_ra_edge_property_map<typename Graph::edge_descriptor> type;
      typedef type const_type;
    };
  };
  template <>
  struct edge_property_selector<edge_list_ra_tag> {
    typedef edge_list_ra_edge_property_selector type;
  };
  template <class G, class EI, class T, class D>
  inline
  typename property_map< edge_list_impl_ra<G,EI,T,D>, edge_index_t>::type
  get(edge_index_t, const edge_list_impl_ra<G,EI,T,D>&) {
    typedef typename property_map< edge_list_impl_ra<G,EI,T,D>,
      edge_index_t>::type EdgeIndexMap;
    return EdgeIndexMap();
  }

  template <class G, class EI, class T, class D>
  inline D
  get(edge_index_t, const edge_list_impl_ra<G,EI,T,D>&,
      typename edge_list_impl_ra<G,EI,T,D>::edge_descriptor e) {
    return e;
  }


  // Some helper classes for determining if the iterators are random access
  template <class Cat>
  struct is_random {
    enum { RET = false };
    typedef mpl::false_ type;
  };
  template <>
  struct is_random<std::random_access_iterator_tag> {
    enum { RET = true }; typedef mpl::true_ type;
  };

  // The edge_list class conditionally inherits from one of the
  // above two classes.

  template <class EdgeIter,
#if !defined BOOST_NO_STD_ITERATOR_TRAITS
            class T = typename std::iterator_traits<EdgeIter>::value_type,
            class D = typename std::iterator_traits<EdgeIter>::difference_type,
            class Cat = typename std::iterator_traits<EdgeIter>::iterator_category>
#else
            class T,
            class D,
            class Cat>
#endif
  class edge_list
    : public mpl::if_< typename is_random<Cat>::type,
                    edge_list_impl_ra< edge_list<EdgeIter,T,D,Cat>, EdgeIter,T,D>,
                    edge_list_impl< edge_list<EdgeIter,T,D,Cat>, EdgeIter,T,D>
             >::type
  {
  public:
    typedef directed_tag directed_category;
    typedef allow_parallel_edge_tag edge_parallel_category;
    typedef edge_list_graph_tag traversal_category;
    typedef std::size_t edges_size_type;
    typedef std::size_t vertices_size_type;
    typedef std::size_t degree_size_type;
    edge_list(EdgeIter first, EdgeIter last) : _first(first), _last(last) {
      m_num_edges = std::distance(first, last);
    }
    edge_list(EdgeIter first, EdgeIter last, edges_size_type E)
      : _first(first), _last(last), m_num_edges(E) { }

    EdgeIter _first, _last;
    edges_size_type m_num_edges;
  };

  template <class EdgeIter, class T, class D, class Cat>
  std::size_t num_edges(const edge_list<EdgeIter, T, D, Cat>& el) {
    return el.m_num_edges;
  }

#ifndef BOOST_NO_STD_ITERATOR_TRAITS
  template <class EdgeIter>
  inline edge_list<EdgeIter>
  make_edge_list(EdgeIter first, EdgeIter last)
  {
    return edge_list<EdgeIter>(first, last);
  }
#endif

} /* namespace boost */

#endif /* BOOST_GRAPH_EDGE_LIST_HPP */
