//  (C) Copyright Joel de Guzman 2003.

//  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 VECTOR_INDEXING_SUITE_JDG20036_HPP

# define VECTOR_INDEXING_SUITE_JDG20036_HPP



# include <boost/python/suite/indexing/indexing_suite.hpp>

# include <boost/python/suite/indexing/container_utils.hpp>

# include <boost/python/iterator.hpp>



namespace boost { namespace python {

            

    // Forward declaration

    template <class Container, bool NoProxy, class DerivedPolicies>

    class vector_indexing_suite;

    

    namespace detail

    {

        template <class Container, bool NoProxy>

        class final_vector_derived_policies 

            : public vector_indexing_suite<Container, 

                NoProxy, final_vector_derived_policies<Container, NoProxy> > {};

    }



    // The vector_indexing_suite class is a predefined indexing_suite derived 

    // class for wrapping std::vector (and std::vector like) classes. It provides

    // all the policies required by the indexing_suite (see indexing_suite).

    // Example usage:

    //

    //  class X {...};

    //

    //  ...

    //

    //      class_<std::vector<X> >("XVec")

    //          .def(vector_indexing_suite<std::vector<X> >())

    //      ;

    //

    // By default indexed elements are returned by proxy. This can be

    // disabled by supplying *true* in the NoProxy template parameter.

    //

    template <

        class Container, 

        bool NoProxy = false,

        class DerivedPolicies 

            = detail::final_vector_derived_policies<Container, NoProxy> >

    class vector_indexing_suite 

        : public indexing_suite<Container, DerivedPolicies, NoProxy>

    {

    public:

    

        typedef typename Container::value_type data_type;

        typedef typename Container::value_type key_type;

        typedef typename Container::size_type index_type;

        typedef typename Container::size_type size_type;

        typedef typename Container::difference_type difference_type;

        

        template <class Class>

        static void 

        extension_def(Class& cl)

        {

            cl

                .def("append", &base_append)

                .def("extend", &base_extend)

            ;

        }

        

        static 

        typename mpl::if_<

            is_class<data_type>

          , data_type&

          , data_type

        >::type

        get_item(Container& container, index_type i)

        { 

            return container[i];

        }



        static object 

        get_slice(Container& container, index_type from, index_type to)

        { 

            if (from > to)

                return object(Container());

            return object(Container(container.begin()+from, container.begin()+to));

        }



        static void 

        set_item(Container& container, index_type i, data_type const& v)

        { 

            container[i] = v;

        }



        static void 

        set_slice(Container& container, index_type from, 

            index_type to, data_type const& v)

        { 

            if (from > to) {

                return;

            }

            else {

                container.erase(container.begin()+from, container.begin()+to);

                container.insert(container.begin()+from, v);

            }

        }



        template <class Iter>

        static void 

        set_slice(Container& container, index_type from, 

            index_type to, Iter first, Iter last)

        { 

            if (from > to) {

                container.insert(container.begin()+from, first, last);

            }

            else {

                container.erase(container.begin()+from, container.begin()+to);

                container.insert(container.begin()+from, first, last);

            }

        }



        static void 

        delete_item(Container& container, index_type i)

        { 

            container.erase(container.begin()+i);

        }

        

        static void 

        delete_slice(Container& container, index_type from, index_type to)

        { 

            if (from > to) {

                // A null-op.

                return;

            }

            container.erase(container.begin()+from, container.begin()+to);

        }

        

        static size_t

        size(Container& container)

        {

            return container.size();

        }

        

        static bool

        contains(Container& container, key_type const& key)

        {

            return std::find(container.begin(), container.end(), key)

                != container.end();

        }

        

        static index_type

        get_min_index(Container& /*container*/)

        { 

            return 0;

        }



        static index_type

        get_max_index(Container& container)

        { 

            return container.size();

        }

      

        static bool 

        compare_index(Container& /*container*/, index_type a, index_type b)

        {

            return a < b;

        }

        

        static index_type

        convert_index(Container& container, PyObject* i_)

        { 

            extract<long> i(i_);

            if (i.check())

            {

                long index = i();

                if (index < 0)

                    index += DerivedPolicies::size(container);

                if (index >= long(container.size()) || index < 0)

                {

                    PyErr_SetString(PyExc_IndexError, "Index out of range");

                    throw_error_already_set();

                }

                return index;

            }

            

            PyErr_SetString(PyExc_TypeError, "Invalid index type");

            throw_error_already_set();

            return index_type();

        }

      

        static void 

        append(Container& container, data_type const& v)

        { 

            container.push_back(v);

        }

        

        template <class Iter>

        static void 

        extend(Container& container, Iter first, Iter last)

        { 

            container.insert(container.end(), first, last);

        }

        

    private:

    

        static void

        base_append(Container& container, object v)

        {

            extract<data_type&> elem(v);

            // try if elem is an exact Data

            if (elem.check())

            {

                DerivedPolicies::append(container, elem());

            }

            else

            {

                //  try to convert elem to data_type

                extract<data_type> elem(v);

                if (elem.check())

                {

                    DerivedPolicies::append(container, elem());

                }

                else

                {

                    PyErr_SetString(PyExc_TypeError, 

                        "Attempting to append an invalid type");

                    throw_error_already_set();

                }

            }

        }

        

        static void

        base_extend(Container& container, object v)

        {

            std::vector<data_type> temp;

            container_utils::extend_container(temp, v);

            DerivedPolicies::extend(container, temp.begin(), temp.end());

        }

    };

       

}} // namespace boost::python 



#endif // VECTOR_INDEXING_SUITE_JDG20036_HPP

