/*=============================================================================
    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;

///////////////////////////////////////////////////////////////////////////
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
    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)
    {
        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
