// Copyright (c) 2003 Raoul M. Gough
//
// Use, modification and distribution is subject to 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)
//
// Header file iterator_range.hpp
//
// Emulate an STL container using a pair of iterators. Doesn't support
// insertion or deletion, for the obvious reasons.
//
// History
// =======
// 2003/ 9/ 9   rmg     File creation as iterator_pair.hpp
// 2003/10/27   rmg     Renamed iterator_range.hpp
// 2008/12/08   Roman   Change indexing suite layout
//
// $Id: iterator_range.hpp,v 1.1.2.7 2004/02/08 18:57:42 raoulgough Exp $
//

#ifndef BOOST_PYTHON_INDEXING_ITERATOR_RANGE_HPP
#define BOOST_PYTHON_INDEXING_ITERATOR_RANGE_HPP

#include <stdexcept>
#include <algorithm>
#include <utility>
#include <boost/type_traits.hpp>
#include <boost/type_traits/ice.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/iterator/iterator_traits.hpp>
#include <indexing_suite/container_traits.hpp>
#include <indexing_suite/container_suite.hpp>
#include <indexing_suite/algorithms.hpp>
#include <indexing_suite/iterator_traits.hpp>

namespace boost { namespace python { namespace indexing {
  template<typename Iterator>
  class iterator_range
  {
  private:
    typedef typename BOOST_PYTHON_INDEXING_CALL_TRAITS <Iterator>::param_type
        iterator_param;

    typedef ::boost::detail::iterator_traits<Iterator> std_traits;

  public:
    typedef typename std_traits::reference       reference;
    typedef Iterator                             iterator;
    typedef typename std_traits::difference_type size_type;
    typedef typename std_traits::difference_type difference_type;
    typedef typename std_traits::value_type      value_type;
    typedef typename std_traits::pointer         pointer;

    typedef iterator                             const_iterator;
    // Can't tell what the const version of our iterator should
    // be. The client code will have to instantiate iterator_range
    // directly with a const_iterator if that's what it wants.

    // Also can't provide: allocator_type, reverse_iterator or
    // const_reverse_iterator. Could probably provide (but don't)
    // const_reference and const_pointer. These would be the same as
    // reference and pointer if Iterator is itself a const_iterator.

  public:
    iterator_range (iterator_param, iterator_param);
    iterator_range (std::pair<iterator, iterator> const &);

    iterator begin() const;
    iterator end() const;

  public:
    // Only sensible for random_access iterators
    size_type size () const;
    reference operator[] (size_type) const;
    reference at (size_type) const;

  private:
    iterator m_begin;
    iterator m_end;
  };

  // Array support function(s).
  template<typename T> iterator_range<T *> make_iterator_range (T *, T*);

#if !BOOST_WORKAROUND (BOOST_MSVC, <= 1200)
  template<typename T, std::size_t N> iterator_range<T *> make_iterator_range(
      T (&array)[N]);

  template<typename T, std::size_t N> T *begin (T (&array)[N]);
  template<typename T, std::size_t N> T *end   (T (&array)[N]);

# define BOOST_MAKE_ITERATOR_RANGE \
      ::boost::python::indexing::make_iterator_range

#else
  // For compilers that can't deduce template argument array bounds
# define BOOST_MAKE_ITERATOR_RANGE(array) \
      ::boost::python::indexing::make_iterator_range ( \
          (array), ((array) + sizeof(array) / sizeof((array)[0])))
#endif

  template<typename Iterator>
  iterator_range<Iterator>::iterator_range(
      iterator_param begin, iterator_param end)
    : m_begin (begin),
    m_end (end)
  {
  }

  template<typename Iterator>
  iterator_range<Iterator>
  ::iterator_range (std::pair<iterator, iterator> const &pair)
    : m_begin (pair.first),
    m_end (pair.second)
  {
  }

  template<typename Iterator>
  typename iterator_range<Iterator>::iterator
  iterator_range<Iterator>::begin() const
  {
    return m_begin;
  }

  template<typename Iterator>
  typename iterator_range<Iterator>::iterator
  iterator_range<Iterator>::end() const
  {
    return m_end;
  }

  template<typename Iterator>
  typename iterator_range<Iterator>::size_type
  iterator_range<Iterator>::size() const
  {
    return std::distance (begin(), end());
  }

  template<typename Iterator>
  typename iterator_range<Iterator>::reference
  iterator_range<Iterator>::operator[](size_type index) const
  {
    iterator temp (begin());
    std::advance (temp, index);
    return *temp;
  }

  template<typename Iterator>
  typename iterator_range<Iterator>::reference
  iterator_range<Iterator>::at (size_type index) const
  {
    if (index >= size())
      {
        throw std::out_of_range
          (std::string ("iterator_range: index out of range"));
      }

    else
      {
        return (*this)[index];
      }
  }

  template<typename T> iterator_range<T *> make_iterator_range (T *p1, T* p2)
  {
    return iterator_range<T *> (p1, p2);
  }

#if !BOOST_WORKAROUND (BOOST_MSVC, <= 1200)
  template<typename T, std::size_t N>
  T *begin (T (&array)[N]) {
    return array;
  }

  template<typename T, std::size_t N>
  T *end (T (&array)[N]) {
    return array + N;
  }

  template<typename T, std::size_t N>
  iterator_range<T *> make_iterator_range (T (&array)[N]) {
    return iterator_range<T *>(begin (array), end (array));
  }
#endif

  template<typename Container, typename ValueTraits = detail::no_override>
  class iterator_range_traits
    : public base_container_traits<Container, ValueTraits>
  {
    typedef base_container_traits<Container, ValueTraits> base_class;

    typedef ::boost::python::indexing::iterator_traits<
      typename Container::iterator
    > iterator_traits_type;

  public:
    typedef typename base_class::value_traits_type value_traits_type;

  private:
    // Methods that we *can't* support because of our value type
    BOOST_STATIC_CONSTANT(
        method_set_type,
        disabled_methods = (
            detail::method_set_if<
               type_traits::ice_not<
                   value_traits_type::equality_comparable
               >::value,
                 method_index      // Impossible if !equality_comparable
               | method_contains   // Impossible if !equality_comparable
               | method_count      // Impossible if !equality_comparable
            >::value

          | detail::method_set_if<
               type_traits::ice_not<
                   value_traits_type::less_than_comparable
               >::value,
               method_sort         // Impossible if !less_than_comparable
            >::value
        ));

  public:
    BOOST_STATIC_CONSTANT(
        method_set_type,
        supported_methods =
        iterator_traits_type::supported_methods & ~disabled_methods);
  };

#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
  namespace detail {
    ///////////////////////////////////////////////////////////////////////
    // algorithms support for iterator_range instances
    ///////////////////////////////////////////////////////////////////////

    template <typename Iterator>
    class algorithms_selector<iterator_range<Iterator> >
    {
      typedef iterator_range<Iterator> Container;

      typedef iterator_range_traits<Container>       mutable_traits;
      typedef iterator_range_traits<Container const> const_traits; // ?

    public:
      typedef default_algorithms<mutable_traits> mutable_algorithms;
      typedef default_algorithms<const_traits>   const_algorithms;
    };
  }
#endif

  template<
    class Container,
    method_set_type MethodMask = all_methods,
    class Traits = iterator_range_traits<Container>
  >
  struct iterator_range_suite
    : container_suite<Container, MethodMask, default_algorithms<Traits> >
  {
  };

} } }

#endif // BOOST_PYTHON_INDEXING_ITERATOR_RANGE_HPP
