// 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 int_slice_helper.hpp
//
// History
// =======
// 2003/10/13   rmg     File creation
// 2008/12/08   Roman   Change indexing suite layout
//
// $Id: int_slice_helper.hpp,v 1.1.2.10 2004/02/08 18:57:42 raoulgough Exp $
//

#ifndef BOOST_PYTHON_INDEXING_INT_SLICE_HELPER_HPP
#define BOOST_PYTHON_INDEXING_INT_SLICE_HELPER_HPP

# include <boost/python/errors.hpp>
# include <indexing_suite/workaround.hpp>

namespace boost { namespace python { namespace indexing {
  template<typename Algorithms, typename SliceType>
  struct int_slice_helper
  {
    // Works with a SliceType that provides an int-like index_type
    // that is convertible to the algorithm's index_param

    typedef Algorithms algorithms_type;
    typedef SliceType slice_type;

    typedef typename algorithms_type::container container;
    typedef typename algorithms_type::reference reference;
    typedef typename algorithms_type::value_param value_param;
    typedef typename algorithms_type::container_traits container_traits;
    typedef typename slice_type::index_type index_type;

    int_slice_helper (container &c, slice_type const &);

    bool next();

    reference current () const;
    void write (value_param val);
    void erase_remaining () const;

  private:
    void assign (value_param val) const;
    void insert (value_param val);

  private:
    slice_type m_slice;
    container *m_ptr;
    index_type m_pos;
  };

  template<typename Algorithms, typename SliceType>
  int_slice_helper<Algorithms, SliceType>
  ::int_slice_helper (container &c, slice_type const &sl)
    : m_slice (sl),
    m_ptr (&c),
    m_pos (-1)
  {
  }

  template<typename Algorithms, typename SliceType>
  bool
  int_slice_helper<Algorithms, SliceType>::next()
  {
    bool result = false; // Assume the worst

    if (m_pos == -1)
      {
        // First time call - get to start of the slice (if any)
        m_pos = m_slice.start();
        result = m_slice.in_range (m_pos);
      }

    else if (m_slice.in_range (m_pos))
      {
        // Subsequent calls - advance by the slice's stride
        m_pos += m_slice.step();
        result = m_slice.in_range (m_pos);
      }

    return result;
  }

  template<typename Algorithms, typename SliceType>
  typename int_slice_helper<Algorithms, SliceType>::reference
  int_slice_helper<Algorithms, SliceType>::current () const
  {
    return algorithms_type::get (*m_ptr, m_pos);
  }

  template<typename Algorithms, typename SliceType>
  void int_slice_helper<Algorithms, SliceType>::write (value_param val)
  {
    if (next())
      {
        assign (val);
      }

    else
      {
        insert (val);
      }
  }

  template<typename Algorithms, typename SliceType>
  void int_slice_helper<Algorithms, SliceType>::assign (value_param val) const
  {
    algorithms_type::assign (*m_ptr, m_pos, val);
  }

  namespace detail {
    template<bool doit> struct maybe_insert {

      // NOTE: use the name "apply_" instead of "apply" to avoid
      // weirdo compiler crash in mpl/aux_/apply.hpp on MSVC7. Don't
      // even ask how I arrived at this fix :-)

      template<class Algorithms>
      static void apply_(
# if defined (BOOST_NO_MEMBER_TEMPLATES) \
        && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          // Can't explicitly instantiate member function - must let
          // the compiler deduce the argument type from a dummy
          // parameter. Same applies throughout
          Algorithms *,
# endif
          typename Algorithms::container &,
          typename Algorithms::index_param,
          typename Algorithms::value_param)
      {
        PyErr_SetString(
            PyExc_TypeError,
            "container does not support insertion into slice");

        boost::python::throw_error_already_set ();
      }
    };

    template<> struct maybe_insert<true> {
      template<class Algorithms>
      static void apply_(
# if defined (BOOST_NO_MEMBER_TEMPLATES) \
        && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          Algorithms *,
# endif
          typename Algorithms::container &c,
          typename Algorithms::index_param i,
          typename Algorithms::value_param v)
      {
        Algorithms::insert (c, i, v);
      }
    };
  }

  template<typename Algorithms, typename SliceType>
  void int_slice_helper<Algorithms, SliceType>::insert (value_param val)
  {
    if (m_slice.step() != 1)
      {
        PyErr_SetString(
            PyExc_ValueError, "attempt to insert via extended slice");

        boost::python::throw_error_already_set ();
      }

    else
      {
        detail::maybe_insert<
          detail::is_member<
            container_traits::supported_methods, method_insert>::value>::
# if defined (BOOST_NO_MEMBER_TEMPLATES) \
  && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          apply_ (static_cast<Algorithms *>(0),
# else
          BOOST_NESTED_TEMPLATE apply_ <Algorithms>(
# endif
              *m_ptr, m_pos, val);

        ++m_pos;  // Advance for any subsequent inserts
      }
  }

  namespace detail {
    template<bool doit> struct maybe_erase {
      template<class Algorithms>
      static void apply_(
# if defined (BOOST_NO_MEMBER_TEMPLATES) \
        && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          Algorithms *,
# endif
          typename Algorithms::container &,
          typename Algorithms::index_param,
          typename Algorithms::index_param)
      {
        PyErr_SetString(
            PyExc_TypeError, "container does not support item deletion");

        boost::python::throw_error_already_set ();
      }
    };

    template<> struct maybe_erase<true> {
      template<class Algorithms>
      static void apply_(
# if defined (BOOST_NO_MEMBER_TEMPLATES) \
        && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          Algorithms *,
# endif
          typename Algorithms::container &c,
          typename Algorithms::index_param from,
          typename Algorithms::index_param to)
      {
        Algorithms::erase_range (c, from, to);
      }
    };
  }

  template<typename Algorithms, typename SliceType>
  void int_slice_helper<Algorithms, SliceType>::erase_remaining () const
  {
    if (m_slice.step() != 1)
      {
        PyErr_SetString(
            PyExc_ValueError, "attempt to delete via extended slice");

        boost::python::throw_error_already_set ();
      }

    else
      {
        detail::maybe_erase<
          detail::is_member<
            container_traits::supported_methods, method_delitem>::value>::

# if defined (BOOST_NO_MEMBER_TEMPLATES) \
  && defined (BOOST_MSVC6_MEMBER_TEMPLATES)
          apply_ (static_cast<Algorithms *>(0),
# else
          BOOST_NESTED_TEMPLATE apply_ <Algorithms>(
# endif
              *m_ptr, m_pos, m_slice.stop());
      }
  }

} } }

#endif // BOOST_PYTHON_INDEXING_INT_SLICE_HELPER_HPP
