// -*- mode:c++ -*-
//
// Header file iterator_traits.hpp
//
// Traits information about iterators for use in determining what
// Python methods to support for a container.
//
// 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)
//
// History
// =======
// 2003/ 8/23   rmg     File creation as iterator_suite.hpp
// 2003/ 9/12   rmg     Renamed iterator_traits.hpp
//
// $Id: iterator_traits.hpp,v 1.1.2.14 2004/02/08 18:57:42 raoulgough Exp $
//

#ifndef BOOST_PYTHON_INDEXING_ITERATOR_TRAITS_HPP
#define BOOST_PYTHON_INDEXING_ITERATOR_TRAITS_HPP

#include <boost/python/suite/indexing/suite_utils.hpp>
#include <boost/python/suite/indexing/methods.hpp>
//#include <boost/python/suite/indexing/workaround.hpp>

//#include <boost/call_traits.hpp>
//#include <boost/type_traits.hpp>
#include <boost/iterator/iterator_traits.hpp>
#include <boost/iterator/iterator_categories.hpp>

namespace boost { namespace python { namespace indexing {
  //////////////////////////////////////////////////////////////////////////
  // Indexing traits common base class
  //////////////////////////////////////////////////////////////////////////

  template<typename Iterator>
  struct base_iterator_traits
  {
  private:
    typedef ::boost::detail::iterator_traits<Iterator> std_traits;
    typedef typename std_traits::reference reference;

  public:
    //    typedef Iterator                             iterator;
    //    typedef typename std_traits::value_type      value_type;
    //    typedef typename std_traits::difference_type difference_type;

    BOOST_STATIC_CONSTANT(
        bool, has_mutable_ref = is_mutable_ref<reference>::value);
  };

  //////////////////////////////////////////////////////////////////////////
  // Indexing traits for input iterators
  //////////////////////////////////////////////////////////////////////////

  template<typename Iterator>
  struct input_iterator_traits : base_iterator_traits<Iterator>
  {
    BOOST_STATIC_CONSTANT(
        method_set_type,
        supported_methods = (
            method_iter
        ));
  };

  //////////////////////////////////////////////////////////////////////////
  // Indexing traits for forward iterators
  //////////////////////////////////////////////////////////////////////////

  template<typename Iterator>
  struct forward_iterator_traits
    : public base_iterator_traits<Iterator>
  {
    BOOST_STATIC_CONSTANT(
        method_set_type,
        supported_methods = (
              method_len
            | method_iter
        ));
  };

  //////////////////////////////////////////////////////////////////////////
  // Indexing traits for bidirectional iterators
  //////////////////////////////////////////////////////////////////////////

  template<typename Iterator>
  struct bidirectional_iterator_traits
    : public forward_iterator_traits<Iterator>
  {
    typedef forward_iterator_traits<Iterator> base_class;

    BOOST_STATIC_CONSTANT(
        method_set_type,
        supported_methods = (
              base_class::supported_methods

            | detail::method_set_if<
                  base_class::has_mutable_ref,
                  method_reverse
              >::value
        ));
  };

  //////////////////////////////////////////////////////////////////////////
  // Indexing traits for random access iterators
  //////////////////////////////////////////////////////////////////////////

  template<typename Iterator>
  struct random_access_iterator_traits
    : public bidirectional_iterator_traits<Iterator>
  {
    typedef bidirectional_iterator_traits<Iterator> base_class;

    BOOST_STATIC_CONSTANT(
        method_set_type,
        supported_methods = (
            (base_class::supported_methods & ~method_iter)

            | method_getitem
            | method_getitem_slice

            | detail::method_set_if<
                  base_class::has_mutable_ref,
                  method_setitem
                | method_setitem_slice
              >::value

            | method_index     // Assumes value is equality_comparable
            | method_contains  // Assumes value is equality_comparable
            | method_count     // Assumes value is equality_comparable

            | detail::method_set_if<
                  base_class::has_mutable_ref,
                  method_sort
              >::value         // Assumes value is less_than_comparable

        ));
  };

  namespace iterator_detail {
    // Some meta-progamming machinery to select the right
    // indexing::iterator_traits template on the basis of
    // BOOST_ITERATOR_CATEGORY

    typedef char basic_iter_sizer[1];
    typedef char forward_iter_sizer[2];
    typedef char bidirectional_iter_sizer[3];
    typedef char random_access_iter_sizer[4];

    basic_iter_sizer         &sizer (void *);
    forward_iter_sizer       &sizer (std::forward_iterator_tag *);
    bidirectional_iter_sizer &sizer (std::bidirectional_iterator_tag *);
    random_access_iter_sizer &sizer (std::random_access_iterator_tag *);
  
    template<size_t Size> struct traits_by_size {
      // Default implementation gives only the most basic support
      template<typename Iterator>
        struct traits {
          typedef input_iterator_traits<Iterator> type;
        };
    };

    template<>
    struct traits_by_size<sizeof(forward_iter_sizer)> {
      template<typename Iterator>
        struct traits {
          typedef forward_iterator_traits<Iterator> type;
        };
    };

    template<>
    struct traits_by_size<sizeof(bidirectional_iter_sizer)> {
      template<typename Iterator>
        struct traits {
          typedef bidirectional_iterator_traits<Iterator> type;
        };
    };

    template<>
    struct traits_by_size<sizeof(random_access_iter_sizer)> {
      template<typename Iterator>
        struct traits {
          typedef random_access_iterator_traits<Iterator> type;
        };
    };

    template<typename Iterator>
    class deduced_traits {
      typedef typename ::boost::BOOST_ITERATOR_CATEGORY<Iterator>::type
          category;

      BOOST_STATIC_CONSTANT(
          size_t, sizer_result = sizeof (sizer (new category)));

    public:
      typedef typename traits_by_size<sizer_result>
        ::BOOST_NESTED_TEMPLATE traits<Iterator>::type type;
    };
  }

  template<typename Iterator>
  struct iterator_traits
    : public iterator_detail::deduced_traits<Iterator>::type
  {
  };
} } }

#endif // BOOST_PYTHON_INDEXING_ITERATOR_TRAITS_HPP
