//  Boost string_algo library collection_traits.hpp header file  -----------------------//

//  Copyright Pavol Droba 2002-2003. 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)

//  See http://www.boost.org for updates, documentation, and revision history.

#ifndef BOOST_STRING_DETAIL_COLLECTION_TRAITS_HPP
#define BOOST_STRING_DETAIL_COLLECTION_TRAITS_HPP

#include <boost/algorithm/string/config.hpp>
#include <cstddef>
#include <string>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/detail/iterator.hpp>
#include <boost/algorithm/string/yes_no_type.hpp>

// Container traits implementation ---------------------------------------------------------

namespace boost {
    namespace algorithm {
        namespace detail {

// Default collection traits -----------------------------------------------------------------

            // Default collection helper 
            /*
                Wraps std::container compliant containers
            */
            template< typename ContainerT >     
            struct default_container_traits
            {
                typedef BOOST_STRING_TYPENAME ContainerT::value_type value_type;
                typedef BOOST_STRING_TYPENAME ContainerT::iterator iterator;
                typedef BOOST_STRING_TYPENAME ContainerT::const_iterator const_iterator;
                typedef BOOST_STRING_TYPENAME 
                    ::boost::mpl::if_< ::boost::is_const<ContainerT>,
                        const_iterator,
                        iterator 
                    >::type result_iterator;
                typedef BOOST_STRING_TYPENAME ContainerT::difference_type difference_type;
                typedef BOOST_STRING_TYPENAME ContainerT::size_type size_type;
                
                // static operations
                template< typename C >
                static size_type size( const C& c )
                {
                    return c.size();
                }

                template< typename C >
                static bool empty( const C& c )
                {
                    return c.empty();
                }

#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename C >
                static iterator begin( C& c )
                {
                    return c.begin();
                }

                template< typename C >
                static const_iterator begin( const C& c )
                {
                    return c.begin();
                }

                template< typename C >
                static iterator end( C& c )
                {
                    return c.end();
                }

                template< typename C >
                static const_iterator end( const C& c )
                {
                    return c.end();
                }

#else // BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename C >
                static result_iterator begin( C& c )
                {
                    return c.begin();
                }

                template< typename C >
                static result_iterator end( C& c )
                {
                    return c.end();
                }

#endif // BOOST_NO_FUNCTION_TEMPLATE_ORDERING    

            }; 

            template<typename T>
            struct default_container_traits_selector
            {
                typedef default_container_traits<T> type;
            };

// Pair container traits ---------------------------------------------------------------------
                    
            // pair selector
            template< typename T, typename U >
            yes_type is_pair_impl( const std::pair<T,U>* );
            no_type is_pair_impl( ... );

            template<typename T> struct is_pair
            {
            private:
                static T* t;
            public:
                BOOST_STATIC_CONSTANT( bool, value=
                    sizeof(is_pair_impl(t))==sizeof(yes_type) );
            };

            // pair helper
            template< typename PairT >
            struct pair_container_traits
            {
                typedef BOOST_STRING_TYPENAME PairT::first_type element_type;

                typedef BOOST_STRING_TYPENAME ::boost::detail::
                    iterator_traits<element_type>::value_type value_type;
                typedef std::size_t size_type;
                typedef BOOST_STRING_TYPENAME ::boost::detail::
                    iterator_traits<element_type>::difference_type difference_type;

                typedef element_type iterator;
                typedef element_type const_iterator;
                typedef element_type result_iterator;

                // static operations
                template< typename P >
                static size_type size( const P& p )
                {
                    difference_type diff = std::distance( p.first, p.second );
                    if ( diff < 0 ) 
                        return 0;
                    else
                        return diff;
                }

                template< typename P >
                static bool empty( const P& p )
                {
                    return p.first==p.second;
                }

                template< typename P > 
                static const_iterator begin( const P& p )
                {
                    return p.first;
                }

                template< typename P >
                static const_iterator end( const P& p )
                {
                    return p.second;
                }
            }; // 'pair_container_helper'

            template<typename T>
            struct pair_container_traits_selector
            {
                typedef pair_container_traits<T> type;
            };

// Array container traits ---------------------------------------------------------------

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
            // array traits ( partial specialization )
            template< typename T >
            struct array_traits;

            template< typename T, std::size_t sz >
            struct array_traits<T[sz]>
            {
                // typedef
                typedef T* iterator;
                typedef const T* const_iterator;
                typedef T value_type;
                typedef std::size_t size_type;
                typedef std::ptrdiff_t difference_type;

                // size of the array ( static );
                BOOST_STATIC_CONSTANT( size_type, array_size = sz );
            };

#else // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

            // array traits ( no partial specialization )
            /*
                without parial specialization we are able to
                provide support only for a limited number of
                types. Currently the primitive numeric types 
                are supported
            */
            template< typename T, typename BaseT >
            struct array_traits_impl
            {
                typedef BaseT value_type;
                typedef BaseT* iterator;
                typedef const BaseT* const_iterator;
                typedef std::size_t size_type;
                typedef std::ptrdiff_t difference_type;

                // size of the array
                BOOST_STATIC_CONSTANT( size_type, array_size = sizeof(T)/sizeof(BaseT) );
            };
            
            template< typename T, typename BaseT >
            struct array_traits_impl_selector
            {
                typedef array_traits_impl<T,BaseT> type;
            };

            struct array_traits_void
            {
                typedef void type;
            };

            template< typename T, typename BaseT >
            struct array_traits_cv_selector
            {
                typedef BOOST_STRING_TYPENAME 
                    ::boost::mpl::eval_if< 
                        ::boost::is_convertible<T,BaseT*>,
                        array_traits_impl_selector<T,BaseT>,
                        ::boost::mpl::eval_if< 
                            ::boost::is_convertible<T,const BaseT*>,
                                array_traits_impl_selector<T, const BaseT>,
                                ::boost::mpl::eval_if< 
                                    ::boost::is_convertible<T, volatile BaseT*>,
                                    array_traits_impl_selector<T, volatile BaseT>,
                                    array_traits_impl_selector<T, const volatile BaseT>
                                >
                            >
                    >::type type;
            };

            template< typename T >
            struct array_traits_select
            {
                template< typename T1, typename T2 >
                struct apply
                {
                    typedef BOOST_STRING_TYPENAME
                        ::boost::mpl::eval_if< 
                            ::boost::is_convertible<T,const volatile T2*>,
                            array_traits_cv_selector<T,T2>,
                            ::boost::mpl::identity<T1> >::type type;
                };
            };

            template< typename T >
            struct array_traits_selector 
            {
            private:
                // supported array base types
#ifndef BOOST_NO_INTRINSIC_WCHAR_T
                typedef BOOST_STRING_TYPENAME
                    ::boost::mpl::vector10<
                        wchar_t,
#else // BOOST_NO_INTRINSIC_WCHAR_T
                typedef BOOST_STRING_TYPENAME
                    ::boost::mpl::vector9<
#endif // BOOST_NO_INTRINSIC_WCHAR_T
                        char,
                        signed char,
                        unsigned char,
                        signed short,
                        unsigned short,
                        signed int,
                        unsigned int,
                        signed long,
                        unsigned long
                >::type array_base_types;

            public:
                typedef BOOST_STRING_TYPENAME
                    ::boost::mpl::fold<
                        array_base_types,
                        ::boost::algorithm::detail::array_traits_void,
                        ::boost::algorithm::detail::array_traits_select<T> >::type type;
            };

            template< typename T >
            struct array_traits
            {
                typedef BOOST_STRING_TYPENAME
                    array_traits_selector<T>::type traits_type;

                typedef BOOST_STRING_TYPENAME
                    traits_type::value_type value_type;
                typedef BOOST_STRING_TYPENAME
                    traits_type::iterator iterator;
                typedef BOOST_STRING_TYPENAME
                    traits_type::const_iterator const_iterator;
                typedef BOOST_STRING_TYPENAME
                    traits_type::size_type size_type;
                typedef BOOST_STRING_TYPENAME
                    traits_type::difference_type difference_type;

                BOOST_STATIC_CONSTANT( size_type, array_size = traits_type::array_size );
            };

#endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
            
            // array lenght resolving
            /*
                Lenght of string contained in a static array could
                be different from the size of the array.
                For string processing we need the lenght without
                terminating 0.

                Therefore, the lenght is calulated for char and wchar_t
                using char_traits, rather then simply returning
                the array size.
            */
            template< typename T >
            struct array_length_selector
            {
                template< typename TraitsT >
                struct array_length
                {
                    typedef BOOST_STRING_TYPENAME 
                        TraitsT::size_type size_type;

                    BOOST_STATIC_CONSTANT(
                        size_type,
                        array_size=TraitsT::array_size );

                    template< typename A >
                    static size_type length( const A& )
                    {
                        return array_size;
                    }

                    template< typename A >
                    static bool empty( const A& )
                    {
                        return array_size==0;
                    }
                };
            };

            // specialization for char
            template<>
            struct array_length_selector<char>
            {
                template< typename TraitsT >
                struct array_length
                {
                    typedef BOOST_STRING_TYPENAME 
                        TraitsT::size_type size_type;

                    template< typename A >
                    static size_type length( const A& a )
                    {
                        if ( a==0 ) 
                            return 0;
                        else
                            return std::char_traits<char>::length(a);
                    }
                    
                    template< typename A >
                    static bool empty( const A& a )
                    {
                        return a==0 || a[0]==0;
                    }
                };
            };

            // specialization for wchar_t
            template<>
            struct array_length_selector<wchar_t>
            {
                template< typename TraitsT >
                struct array_length
                {
                    typedef BOOST_STRING_TYPENAME 
                        TraitsT::size_type size_type;

                    template< typename A >
                    static size_type length( const A& a )
                    {
                        if ( a==0 ) 
                            return 0;
                        else
                            return std::char_traits<wchar_t>::length(a);
                    }

                    template< typename A >
                    static bool empty( const A& a )
                    {
                        return a==0 || a[0]==0;
                    }
                };
            };

            template< typename T >
            struct array_container_traits
            {
            private:
                // resolve array traits
                typedef array_traits<T> traits_type;

            public:
                typedef BOOST_STRING_TYPENAME
                    traits_type::value_type value_type;
                typedef BOOST_STRING_TYPENAME
                    traits_type::iterator iterator;
                typedef BOOST_STRING_TYPENAME
                    traits_type::const_iterator const_iterator;
                typedef BOOST_STRING_TYPENAME
                    traits_type::size_type size_type;
                typedef BOOST_STRING_TYPENAME
                    traits_type::difference_type difference_type;

                typedef BOOST_STRING_TYPENAME
                    ::boost::mpl::if_< ::boost::is_const<T>,
                        const_iterator,
                        iterator 
                    >::type result_iterator;
                
            private:
                // resolve array size
                typedef BOOST_STRING_TYPENAME
                    ::boost::remove_cv<value_type>::type char_type;
                typedef BOOST_STRING_TYPENAME
                    array_length_selector<char_type>::
                        BOOST_NESTED_TEMPLATE array_length<traits_type> array_length_type;

            public:
                BOOST_STATIC_CONSTANT( size_type, array_size = traits_type::array_size );

                // static operations
                template< typename A >
                static size_type size( const A& a )
                {
                    return array_length_type::length(a);
                }

                template< typename A >
                static bool empty( const A& a )
                {
                    return array_length_type::empty(a);
                }
                

#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename A >
                static iterator begin( A& a )
                {
                    return a;
                }

                template< typename A >
                static const_iterator begin( const A& a )
                {
                    return a;
                }

                template< typename A >
                static iterator end( A& a )
                {
                    return a+array_length_type::length(a);
                }

                template< typename A >
                static const_iterator end( const A& a )
                {
                    return a+array_length_type::length(a);
                }

#else // BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename A >
                static result_iterator begin( A& a )
                {
                    return a;
                }

                template< typename A >
                static result_iterator end( A& a )
                {
                    return a+array_length_type::length(a);
                }

#endif // BOOST_NO_FUNCTION_TEMPLATE_ORDERING    

            }; 

            template<typename T>
            struct array_container_traits_selector
            {
                typedef array_container_traits<T> type;
            };

// Pointer container traits ---------------------------------------------------------------

            template<typename T>
            struct pointer_container_traits
            {
                typedef BOOST_STRING_TYPENAME
                    ::boost::remove_pointer<T>::type value_type;

                typedef BOOST_STRING_TYPENAME
                    ::boost::remove_cv<value_type>::type char_type;
                typedef ::std::char_traits<char_type> char_traits;

                typedef value_type* iterator;
                typedef const value_type* const_iterator;
                typedef std::ptrdiff_t difference_type;
                typedef std::size_t size_type;

                typedef BOOST_STRING_TYPENAME
                    ::boost::mpl::if_< ::boost::is_const<T>,
                        const_iterator,
                        iterator 
                    >::type result_iterator;

                // static operations
                template< typename P >
                static size_type size( const P& p )
                {
                    if ( p==0 ) 
                        return 0;
                    else
                        return char_traits::length(p);
                }

                template< typename P >
                static bool empty( const P& p )
                {
                    return p==0 || p[0]==0;
                }

#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename P >
                static iterator begin( P& p )
                {
                    return p;
                }

                template< typename P >
                static const_iterator begin( const P& p )
                {
                    return p;
                }

                template< typename P >
                static iterator end( P& p )
                {
                    if ( p==0 )
                        return p;
                    else
                        return p+char_traits::length(p);
                }

                template< typename P >
                static const_iterator end( const P& p )
                {
                    if ( p==0 )
                        return p;
                    else
                        return p+char_traits::length(p);
                }

#else // BOOST_NO_FUNCTION_TEMPLATE_ORDERING

                template< typename P >
                static result_iterator begin( P& p )
                {
                    return p;
                }

                template< typename P >
                static result_iterator end( P& p )
                {
                    if ( p==0 )
                        return p;
                    else
                        return p+char_traits::length(p);
                }

#endif // BOOST_NO_FUNCTION_TEMPLATE_ORDERING    
            }; 

            template<typename T>
            struct pointer_container_traits_selector
            {
                typedef pointer_container_traits<T> type;
            };

        } // namespace detail
    } // namespace algorithm
} // namespace boost


#endif  // BOOST_STRING_DETAIL_COLLECTION_HPP
