/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman
    Copyright (c) 2001-2011 Hartmut Kaiser
    http://spirit.sourceforge.net/

    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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_X3_CONTAINER_FEBRUARY_06_2007_1001AM)
#define BOOST_SPIRIT_X3_CONTAINER_FEBRUARY_06_2007_1001AM

#include <boost/fusion/support/category_of.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/fusion/include/deque.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/type_traits/make_void.hpp>

#include <vector>
#include <string>
#include <iterator>
#include <algorithm>

namespace boost { namespace spirit { namespace x3 { namespace traits
{
    ///////////////////////////////////////////////////////////////////////////
    //  This file contains some container utils for stl containers.
    ///////////////////////////////////////////////////////////////////////////

    namespace detail
    {
        template <typename T, typename Enabler = void>
        struct is_container_impl : mpl::false_ {};

        template <typename T>
        struct is_container_impl<T, void_t<
            typename T::value_type, typename T::iterator,
            typename T::size_type, typename T::reference> > : mpl::true_ {};

        template <typename T, typename Enabler = void>
        struct is_associative_impl : mpl::false_ {};

        template <typename T>
        struct is_associative_impl<T, void_t<typename T::key_type>>
            : mpl::true_ {};
    }

    template <typename T>
    using is_container = typename detail::is_container_impl<T>::type;

    template <typename T>
    using is_associative = typename detail::is_associative_impl<T>::type;

    template<typename T, typename Enable = void>
    struct is_reservable : mpl::false_ {};

    template<typename T>
    struct is_reservable<T, decltype(std::declval<T&>().reserve(0))>
      : mpl::true_ {};

    ///////////////////////////////////////////////////////////////////////////
    namespace detail
    {
        template <typename T>
        struct remove_value_const : mpl::identity<T> {};

        template <typename T>
        struct remove_value_const<T const> : remove_value_const<T> {};

        template <typename F, typename S>
        struct remove_value_const<std::pair<F, S>>
        {
            typedef typename remove_value_const<F>::type first_type;
            typedef typename remove_value_const<S>::type second_type;
            typedef std::pair<first_type, second_type> type;
        };
    }

    ///////////////////////////////////////////////////////////////////////
    template <typename Container, typename Enable = void>
    struct container_value
      : detail::remove_value_const<typename Container::value_type>
    {};

    template <typename Container>
    struct container_value<Container const> : container_value<Container> {};

    // There is no single container value for fusion maps, but because output
    // of this metafunc is used to check wheter parser's attribute can be
    // saved to container, we simply return whole fusion::map as is
    // so that check can be done in traits::is_substitute specialisation
    template <typename T>
    struct container_value<T
			   , typename enable_if<typename mpl::eval_if <
						    fusion::traits::is_sequence<T>
						    , fusion::traits::is_associative<T>
						    , mpl::false_ >::type >::type>
    : mpl::identity<T> {};

    template <>
    struct container_value<unused_type> : mpl::identity<unused_type> {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename Enable = void>
    struct container_iterator
        : mpl::identity<typename Container::iterator> {};

    template <typename Container>
    struct container_iterator<Container const>
         : mpl::identity<typename Container::const_iterator> {};

    template <>
    struct container_iterator<unused_type>
        : mpl::identity<unused_type const*> {};

    template <>
    struct container_iterator<unused_type const>
        : mpl::identity<unused_type const*> {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename T>
    bool push_back(Container& c, T&& val);

    template <typename Container, typename Enable = void>
    struct push_back_container
    {
        template <typename T>
        static bool call(Container& c, T&& val)
        {
            c.insert(c.end(), static_cast<T&&>(val));
            return true;
        }
    };

    template <typename Container, typename T>
    inline bool push_back(Container& c, T&& val)
    {
        return push_back_container<Container>::call(c, static_cast<T&&>(val));
    }

    template <typename Container>
    inline bool push_back(Container&, unused_type)
    {
        return true;
    }

    template <typename T>
    inline bool push_back(unused_type, T&&)
    {
        return true;
    }

    inline bool push_back(unused_type, unused_type)
    {
        return true;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename Iterator>
    bool append(Container& c, Iterator first, Iterator last);

    template <typename Container, typename Enable = void>
    struct append_container
    {
    private:
        template <typename Iterator>
        static void reserve(Container& /* c */, Iterator /* first */, Iterator /* last */, mpl::false_)
        {
            // Not all containers have "reserve"
        }

        template <typename Iterator>
        static void reserve(Container& c, Iterator first, Iterator last, mpl::true_)
        {
            c.reserve(c.size() + std::distance(first, last));
        }

        template <typename Iterator>
        static void insert(Container& c, Iterator first, Iterator last, mpl::false_)
        {
            c.insert(c.end(), first, last);
        }

        template <typename Iterator>
        static void insert(Container& c, Iterator first, Iterator last, mpl::true_)
        {
            c.insert(first, last);
        }

    public:
        template <typename Iterator>
        static bool call(Container& c, Iterator first, Iterator last)
        {
            reserve(c, first, last, is_reservable<Container>{});
            insert(c, first, last, is_associative<Container>{});
            return true;
        }
    };

    template <typename Container, typename Iterator>
    inline bool append(Container& c, Iterator first, Iterator last)
    {
        return append_container<Container>::call(c, first, last);
    }

    template <typename Iterator>
    inline bool append(unused_type, Iterator /* first */, Iterator /* last */)
    {
        return true;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename Enable = void>
    struct is_empty_container
    {
        static bool call(Container const& c)
        {
            return c.empty();
        }
    };

    template <typename Container>
    inline bool is_empty(Container const& c)
    {
        return is_empty_container<Container>::call(c);
    }

    inline bool is_empty(unused_type)
    {
        return true;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename Enable = void>
    struct begin_container
    {
        static typename container_iterator<Container>::type call(Container& c)
        {
            return c.begin();
        }
    };

    template <typename Container>
    inline typename container_iterator<Container>::type
    begin(Container& c)
    {
        return begin_container<Container>::call(c);
    }

    inline unused_type const*
    begin(unused_type)
    {
        return &unused;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename Enable = void>
    struct end_container
    {
        static typename container_iterator<Container>::type call(Container& c)
        {
            return c.end();
        }
    };

    template <typename Container>
    inline typename container_iterator<Container>::type
    end(Container& c)
    {
        return end_container<Container>::call(c);
    }

    inline unused_type const*
    end(unused_type)
    {
        return &unused;
    }


    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Enable = void>
    struct deref_iterator
    {
        typedef typename std::iterator_traits<Iterator>::reference type;
        static type call(Iterator& it)
        {
            return *it;
        }
    };

    template <typename Iterator>
    typename deref_iterator<Iterator>::type
    deref(Iterator& it)
    {
        return deref_iterator<Iterator>::call(it);
    }

    inline unused_type
    deref(unused_type const*)
    {
        return unused;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Enable = void>
    struct next_iterator
    {
        static void call(Iterator& it)
        {
            ++it;
        }
    };

    template <typename Iterator>
    void next(Iterator& it)
    {
        next_iterator<Iterator>::call(it);
    }

    inline void next(unused_type const*)
    {
        // do nothing
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Enable = void>
    struct compare_iterators
    {
        static bool call(Iterator const& it1, Iterator const& it2)
        {
            return it1 == it2;
        }
    };

    template <typename Iterator>
    bool compare(Iterator& it1, Iterator& it2)
    {
        return compare_iterators<Iterator>::call(it1, it2);
    }

    inline bool compare(unused_type const*, unused_type const*)
    {
        return false;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename T>
    struct build_container : mpl::identity<std::vector<T>> {};

    template <typename T>
    struct build_container<boost::fusion::deque<T> > : build_container<T> {};

    template <>
    struct build_container<unused_type> : mpl::identity<unused_type> {};

    template <>
    struct build_container<char> : mpl::identity<std::string> {};

}}}}

#endif
