/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman
    Copyright (c) 2001-2011 Hartmut Kaiser

    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_PRINT_ATTRIBUTE_JANUARY_20_2013_0814AM)
#define BOOST_SPIRIT_X3_PRINT_ATTRIBUTE_JANUARY_20_2013_0814AM

#include <boost/variant.hpp>
#include <boost/optional/optional.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
#include <boost/spirit/home/x3/support/traits/is_variant.hpp>

namespace boost { namespace spirit { namespace x3 { namespace traits
{
    template <typename Out, typename T>
    void print_attribute(Out& out, T const& val);

    template <typename Out>
    inline void print_attribute(Out&, unused_type) {}

    ///////////////////////////////////////////////////////////////////////////
    namespace detail
    {
        template <typename Out>
        struct print_fusion_sequence
        {
            print_fusion_sequence(Out& out)
              : out(out), is_first(true) {}

            typedef void result_type;

            template <typename T>
            void operator()(T const& val) const
            {
                if (is_first)
                    is_first = false;
                else
                    out << ", ";
                x3::traits::print_attribute(out, val);
            }

            Out& out;
            mutable bool is_first;
        };

        // print elements in a variant
        template <typename Out>
        struct print_visitor : static_visitor<>
        {
            print_visitor(Out& out) : out(out) {}

            template <typename T>
            void operator()(T const& val) const
            {
                x3::traits::print_attribute(out, val);
            }

            Out& out;
        };
    }

    template <typename Out, typename T, typename Enable = void>
    struct print_attribute_debug
    {
        // for plain data types
        template <typename T_>
        static void call(Out& out, T_ const& val, unused_attribute)
        {
            out << "unused";
        }

        // for plain data types
        template <typename T_>
        static void call(Out& out, T_ const& val, plain_attribute)
        {
            out << val;
        }

        // for fusion data types
        template <typename T_>
        static void call(Out& out, T_ const& val, tuple_attribute)
        {
            out << '[';
            fusion::for_each(val, detail::print_fusion_sequence<Out>(out));
            out << ']';
        }

        // stl container
        template <typename T_>
        static void call(Out& out, T_ const& val, container_attribute)
        {
            out << '[';
            if (!traits::is_empty(val))
            {
                bool first = true;
                typename container_iterator<T_ const>::type iend = traits::end(val);
                for (typename container_iterator<T_ const>::type i = traits::begin(val);
                     !traits::compare(i, iend); traits::next(i))
                {
                    if (!first)
                        out << ", ";
                    first = false;
                    x3::traits::print_attribute(out, traits::deref(i));
                }
            }
            out << ']';
        }

        // for variant types
        template <typename T_>
        static void call(Out& out, T_ const& val, variant_attribute)
        {
            apply_visitor(detail::print_visitor<Out>(out), val);
        }

        // for optional types
        template <typename T_>
        static void call(Out& out, T_ const& val, optional_attribute)
        {
            if (val)
                x3::traits::print_attribute(out, *val);
            else
                out << "[empty]";
        }

        // main entry point
        static void call(Out& out, T const& val)
        {
            call(out, val, typename attribute_category<T>::type());
        }
    };

    ///////////////////////////////////////////////////////////////////////////
    template <typename Out, typename T>
    inline void print_attribute(Out& out, T const& val)
    {
        print_attribute_debug<Out, T>::call(out, val);
    }
}}}}

#endif
