//  Copyright (c) 2001-2010 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(SPIRIT_KARMA_DUPLICATE_JUL_11_2010_0954AM)
#define SPIRIT_KARMA_DUPLICATE_JUL_11_2010_0954AM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/karma/meta_compiler.hpp>
#include <boost/spirit/home/karma/generator.hpp>
#include <boost/spirit/home/karma/domain.hpp>
#include <boost/spirit/home/karma/detail/attributes.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/support/assert_msg.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/bool.hpp>

namespace boost { namespace spirit
{
    ///////////////////////////////////////////////////////////////////////////
    // Enablers
    ///////////////////////////////////////////////////////////////////////////
    template <>
    struct use_directive<karma::domain, tag::duplicate> // enables duplicate
      : mpl::true_ {};
}}

namespace boost { namespace spirit { namespace karma
{
    using spirit::duplicate;
    using spirit::duplicate_type;

    ///////////////////////////////////////////////////////////////////////////
    namespace detail
    {
        ///////////////////////////////////////////////////////////////////////
        template <typename T
          , bool IsSequence = fusion::traits::is_sequence<T>::value>
        struct attribute_count
          : fusion::result_of::size<T> 
        {};

        template <>
        struct attribute_count<unused_type, false> 
          : mpl::int_<0> 
        {};

        template <typename T>
        struct attribute_count<T, false> 
          : mpl::int_<1> 
        {};

        ///////////////////////////////////////////////////////////////////////
        template <typename T
          , bool IsSequence = fusion::traits::is_sequence<T>::value>
        struct first_attribute_of_subject 
          : fusion::result_of::at_c<T, 0> 
        {};

        template <typename T>
        struct first_attribute_of_subject<T, false> 
          : mpl::identity<T> 
        {};

        template <typename T, typename Context, typename Iterator>
        struct first_attribute_of
          : first_attribute_of_subject<
                typename traits::attribute_of<T, Context, Iterator>::type>
        {};

        ///////////////////////////////////////////////////////////////////////
        template <typename Attribute, typename T, int N>
        struct duplicate_sequence_attribute
        {
            typedef typename fusion::result_of::make_cons<
                reference_wrapper<T const>
              , typename duplicate_sequence_attribute<Attribute, T, N-1>::type
            >::type type;

            static type call(T const& t)
            {
                return fusion::make_cons(cref(t)
                  , duplicate_sequence_attribute<Attribute, T, N-1>::call(t));
            }
        };

        template <typename Attribute, typename T>
        struct duplicate_sequence_attribute<Attribute, T, 1> 
        {
            typedef typename fusion::result_of::make_cons<
                reference_wrapper<T const> >::type type;

            static type call(T const& t)
            {
                return fusion::make_cons(cref(t));
            }
        };

        ///////////////////////////////////////////////////////////////////////
        template <typename Attribute, typename T
          , int N = attribute_count<Attribute>::value
          , bool IsSequence = fusion::traits::is_sequence<Attribute>::value>
        struct duplicate_attribute
        {
            BOOST_SPIRIT_ASSERT_MSG(N > 0, invalid_duplication_count, (Attribute));

            typedef typename duplicate_sequence_attribute<Attribute, T, N>::type
                cons_type;
            typedef typename fusion::result_of::as_vector<cons_type>::type type;

            static type call(T const& t)
            {
                return fusion::as_vector(
                    duplicate_sequence_attribute<Attribute, T, N>::call(t));
            }
        };

        template <typename Attribute, typename T>
        struct duplicate_attribute<Attribute, T, 0, false> 
        {
            typedef unused_type type;

            static type call(T const&)
            {
                return unused;
            }
        };

        template <typename Attribute, typename T, int N>
        struct duplicate_attribute<Attribute, T, N, false>
        {
            typedef Attribute const& type;

            static type call(T const& t)
            {
                return t;
            }
        };
    }

    template <typename Attribute, typename T>
    inline typename detail::duplicate_attribute<Attribute, T>::type
    duplicate_attribute(T const& t)
    { 
        return detail::duplicate_attribute<Attribute, T>::call(t);
    }

    ///////////////////////////////////////////////////////////////////////////
    // duplicate_directive duplicate its attribute for all elements of the 
    // subject generator without generating anything itself
    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject>
    struct duplicate_directive : unary_generator<duplicate_directive<Subject> >
    {
        typedef Subject subject_type;
        typedef typename subject_type::properties properties;

        duplicate_directive(Subject const& subject)
          : subject(subject) {}

        template <typename Context, typename Iterator = unused_type>
        struct attribute
          : detail::first_attribute_of<Subject, Context, Iterator>
        {};

        template <typename OutputIterator, typename Context, typename Delimiter
          , typename Attribute>
        bool generate(OutputIterator& sink, Context& ctx, Delimiter const& d
          , Attribute const& attr) const
        {
            typedef typename traits::attribute_of<Subject, Context>::type 
                subject_attr_type;
            return subject.generate(sink, ctx, d
              , duplicate_attribute<subject_attr_type>(attr));
        }

        template <typename Context>
        info what(Context& context) const
        {
            return info("duplicate", subject.what(context));
        }

        Subject subject;
    };

    ///////////////////////////////////////////////////////////////////////////
    // Generator generators: make_xxx function (objects)
    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject, typename Modifiers>
    struct make_directive<tag::duplicate, Subject, Modifiers>
    {
        typedef duplicate_directive<Subject> result_type;
        result_type operator()(unused_type, Subject const& subject
          , unused_type) const
        {
            return result_type(subject);
        }
    };
}}}

namespace boost { namespace spirit { namespace traits
{
    template <typename Subject>
    struct has_semantic_action<karma::duplicate_directive<Subject> >
      : unary_has_semantic_action<Subject> {};
}}}

#endif
