// Boost.Range library
//
//  Copyright Neil Groves 2014.
//  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)
//
// For more information, see http://www.boost.org/libs/range/
//
#ifndef BOOST_RANGE_ADAPTOR_FORMATTED_HPP_INCLUDED
#define BOOST_RANGE_ADAPTOR_FORMATTED_HPP_INCLUDED

#include <boost/config.hpp>
#include <boost/range/concepts.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/remove_extent.hpp>
#include <ostream>

namespace mars_boost {} namespace boost = mars_boost; namespace mars_boost
{
    namespace range_detail
    {

template<typename Sep, typename Prefix, typename Postfix>
struct formatted_holder
{
    typedef typename mars_boost::mpl::if_<
        mars_boost::is_array<Sep>,
        const typename mars_boost::remove_extent<Sep>::type*,
        Sep
    >::type separator_t;

    typedef typename mars_boost::mpl::if_<
        mars_boost::is_array<Prefix>,
        const typename mars_boost::remove_extent<Prefix>::type*,
        Prefix
    >::type prefix_t;

    typedef typename mars_boost::mpl::if_<
        mars_boost::is_array<Postfix>,
        const typename mars_boost::remove_extent<Postfix>::type*,
        Postfix
    >::type postfix_t;

    formatted_holder(
        const separator_t& sep,
        const prefix_t& prefix,
        const postfix_t& postfix)
        : m_sep(sep)
        , m_prefix(prefix)
        , m_postfix(postfix)
    {
    }

    separator_t m_sep;
    prefix_t m_prefix;
    postfix_t m_postfix;
};

template<typename Iter, typename Sep, typename Prefix, typename Postfix>
class formatted_range
        : public mars_boost::iterator_range<Iter>
{
    typedef formatted_holder<Sep,Prefix,Postfix> holder_t;
public:
    formatted_range(Iter first, Iter last, const holder_t& holder)
        : mars_boost::iterator_range<Iter>(first, last)
        , m_holder(holder)
    {
    }

    template<typename OStream>
    void write(OStream& out) const
    {
        Iter it(this->begin());
        out << m_holder.m_prefix;
        if (it != this->end())
        {
            out << *it;
            for (++it; it != this->end(); ++it)
            {
                out << m_holder.m_sep << *it;
            }
        }
        out << m_holder.m_postfix;
    }

private:
    holder_t m_holder;
};

template<
    typename SinglePassRange,
    typename Sep,
    typename Prefix,
    typename Postfix
>
inline range_detail::formatted_range<
    typename range_iterator<const SinglePassRange>::type, Sep, Prefix, Postfix
>
operator|(
    const SinglePassRange& rng,
    const range_detail::formatted_holder<Sep,Prefix,Postfix>& holder
)
{
    typedef typename range_iterator<const SinglePassRange>::type iterator;
    return range_detail::formatted_range<iterator, Sep, Prefix, Postfix>(
        mars_boost::begin(rng), mars_boost::end(rng), holder);
}

template<typename Char, typename Traits, typename Iter, typename Sep,
    typename Prefix, typename Postfix>
std::basic_ostream<Char, Traits>&
operator<<(
        std::basic_ostream<Char, Traits>& out,
        const formatted_range<Iter, Sep, Prefix, Postfix>& writer)
{
    writer.write(out);
    return out;
}

    } // namespace range_detail

    namespace adaptors
    {

template<typename Sep, typename Prefix, typename Postfix>
range_detail::formatted_holder<Sep, Prefix, Postfix>
formatted(const Sep& sep, const Prefix& prefix, const Postfix& postfix)
{
    return range_detail::formatted_holder<Sep,Prefix,Postfix>(
                sep, prefix, postfix);
}

template<typename Sep, typename Prefix>
range_detail::formatted_holder<Sep, Prefix, char>
formatted(const Sep& sep, const Prefix& prefix)
{
    return range_detail::formatted_holder<Sep, Prefix, char>(sep, prefix, '}');
}

template<typename Sep>
range_detail::formatted_holder<Sep, char, char>
formatted(const Sep& sep)
{
    return range_detail::formatted_holder<Sep, char, char>(sep, '{', '}');
}

inline range_detail::formatted_holder<char, char, char>
formatted()
{
    return range_detail::formatted_holder<char, char, char>(',', '{', '}');
}

using range_detail::formatted_range;

template<typename SinglePassRange, typename Sep, typename Prefix,
         typename Postfix>
inline mars_boost::range_detail::formatted_range<
    typename mars_boost::range_iterator<const SinglePassRange>::type,
    Sep, Prefix, Postfix
>
format(
    const SinglePassRange& rng,
    const Sep& sep,
    const Prefix& prefix,
    const Postfix& postfix
)
{
    typedef typename mars_boost::range_iterator<const SinglePassRange>::type
                iterator_t;

    typedef mars_boost::range_detail::formatted_range<
                iterator_t, Sep, Prefix, Postfix>       result_t;

    typedef mars_boost::range_detail::formatted_holder<Sep, Prefix, Postfix>
                holder_t;

    return result_t(mars_boost::begin(rng), mars_boost::end(rng),
                    holder_t(sep, prefix, postfix));
}

template<typename SinglePassRange, typename Sep, typename Prefix>
inline mars_boost::range_detail::formatted_range<
    typename mars_boost::range_iterator<const SinglePassRange>::type,
    Sep, Prefix, char
>
format(
    const SinglePassRange& rng,
    const Sep& sep,
    const Prefix& prefix)
{
    return adaptors::format<SinglePassRange, Sep, Prefix, char>(rng, sep, prefix, '}');
}

template<typename SinglePassRange, typename Sep>
inline mars_boost::range_detail::formatted_range<
    typename mars_boost::range_iterator<const SinglePassRange>::type,
    Sep, char, char
>
format(const SinglePassRange& rng, const Sep& sep)
{
    return adaptors::format<SinglePassRange, Sep, char, char>(rng, sep, '{', '}');
}

template<typename SinglePassRange>
inline mars_boost::range_detail::formatted_range<
    typename mars_boost::range_iterator<const SinglePassRange>::type,
    char, char, char
>
format(const SinglePassRange& rng)
{
    return adaptors::format<SinglePassRange, char, char, char>(rng, ',', '{', '}');
}

    } // namespace adaptors

    namespace range
    {
        using mars_boost::range_detail::formatted_range;
    } // namespace range
} // namespace mars_boost {} namespace boost = mars_boost; namespace mars_boost

#endif // include guard
