///////////////////////////////////////////////////////////////////////////////
/// \file debug.hpp
/// Utilities for debugging proto expression trees
//
//  Copyright 2007 Eric Niebler. 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)

#ifndef BOOST_PROTO_DEBUG_HPP_EAN_12_31_2006
#define BOOST_PROTO_DEBUG_HPP_EAN_12_31_2006

#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

#if !defined(__WAVE__) || !defined(BOOST_PROTO_DOXYGEN_INVOKED)
#include <iomanip>
#include <iostream>
#include <typeinfo>
#include <boost/xpressive/proto/proto_fwd.hpp>
#include <boost/xpressive/proto/expr.hpp>
#include <boost/xpressive/proto/traits.hpp>
#else
/// INTERNAL ONLY
/// Needed to work around doxygen bug
struct a_dummy_global;
#endif

namespace boost { namespace proto
{
    namespace tag
    {
        namespace hidden_detail_
        {
            typedef char (&not_ostream)[sizeof(std::ostream)+1];
            not_ostream operator<<(std::ostream &, detail::dont_care);

            template<typename Tag, std::size_t S>
            struct printable_tag_
            {
                typedef char const *type;
                static type call() { return typeid(Tag).name(); }
            };

            template<typename Tag>
            struct printable_tag_<Tag, sizeof(std::ostream)>
            {
                typedef Tag type;
                static type call() { return Tag(); }
            };

            template<typename Tag>
            struct printable_tag
              : printable_tag_<Tag, sizeof(std::cout << Tag())>
            {};
        }

        template<typename Tag>
        inline typename hidden_detail_::printable_tag<Tag>::type proto_tag_name(Tag)
        {
            return hidden_detail_::printable_tag<Tag>::call();
        }

    #define BOOST_PROTO_DEFINE_TAG_NAME(Tag)\
        inline char const *proto_tag_name(tag::Tag)\
        {\
            return #Tag;\
        }\
        /**/

        BOOST_PROTO_DEFINE_TAG_NAME(posit)
        BOOST_PROTO_DEFINE_TAG_NAME(negate)
        BOOST_PROTO_DEFINE_TAG_NAME(dereference)
        BOOST_PROTO_DEFINE_TAG_NAME(complement)
        BOOST_PROTO_DEFINE_TAG_NAME(address_of)
        BOOST_PROTO_DEFINE_TAG_NAME(logical_not)
        BOOST_PROTO_DEFINE_TAG_NAME(pre_inc)
        BOOST_PROTO_DEFINE_TAG_NAME(pre_dec)
        BOOST_PROTO_DEFINE_TAG_NAME(post_inc)
        BOOST_PROTO_DEFINE_TAG_NAME(post_dec)
        BOOST_PROTO_DEFINE_TAG_NAME(shift_left)
        BOOST_PROTO_DEFINE_TAG_NAME(shift_right)
        BOOST_PROTO_DEFINE_TAG_NAME(multiplies)
        BOOST_PROTO_DEFINE_TAG_NAME(divides)
        BOOST_PROTO_DEFINE_TAG_NAME(modulus)
        BOOST_PROTO_DEFINE_TAG_NAME(plus)
        BOOST_PROTO_DEFINE_TAG_NAME(minus)
        BOOST_PROTO_DEFINE_TAG_NAME(less)
        BOOST_PROTO_DEFINE_TAG_NAME(greater)
        BOOST_PROTO_DEFINE_TAG_NAME(less_equal)
        BOOST_PROTO_DEFINE_TAG_NAME(greater_equal)
        BOOST_PROTO_DEFINE_TAG_NAME(equal_to)
        BOOST_PROTO_DEFINE_TAG_NAME(not_equal_to)
        BOOST_PROTO_DEFINE_TAG_NAME(logical_or)
        BOOST_PROTO_DEFINE_TAG_NAME(logical_and)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_and)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_or)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_xor)
        BOOST_PROTO_DEFINE_TAG_NAME(comma)
        BOOST_PROTO_DEFINE_TAG_NAME(mem_ptr)
        BOOST_PROTO_DEFINE_TAG_NAME(assign)
        BOOST_PROTO_DEFINE_TAG_NAME(shift_left_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(shift_right_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(multiplies_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(divides_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(modulus_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(plus_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(minus_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_and_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_or_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(bitwise_xor_assign)
        BOOST_PROTO_DEFINE_TAG_NAME(subscript)
        BOOST_PROTO_DEFINE_TAG_NAME(if_else_)
        BOOST_PROTO_DEFINE_TAG_NAME(function)

    #undef BOOST_PROTO_DEFINE_TAG_NAME
    }

    namespace functional
    {
        // Display a proto expression tree
        struct display_expr
        {
            display_expr(int depth = 0, std::ostream &sout = std::cout)
              : depth_(depth)
              , first_(true)
              , sout_(sout)
            {}

            template<typename Args>
            void operator()(expr<tag::terminal, Args, 0> const &expr) const
            {
                this->sout_ << std::setw(this->depth_) << (this->first_? "" : ", ")
                    << "terminal(" << proto::arg(expr) << ")\n";
                this->first_ = false;
            }

        #define BOOST_PROTO_ARG(z, n, data)                                                         \
            display(proto::arg_c<n>(expr));                                                         \
            /**/

        #define BOOST_PP_LOCAL_MACRO(N)                                                             \
            template<typename Tag, typename Args>                                                   \
            void operator()(expr<Tag, Args, N> const &expr) const                                   \
            {                                                                                       \
                using namespace tag;                                                                \
                this->sout_ << std::setw(this->depth_) << (this->first_? "" : ", ")                 \
                    << proto_tag_name(Tag()) << "(\n";                                              \
                display_expr display(this->depth_ + 4, this->sout_);                                \
                BOOST_PP_REPEAT(N, BOOST_PROTO_ARG, _)                                              \
                this->sout_ << std::setw(this->depth_) << "" << ")\n";                              \
                this->first_ = false;                                                               \
            }                                                                                       \
            /**/

        #define BOOST_PP_LOCAL_LIMITS (1, BOOST_PROTO_MAX_ARITY)
        #include BOOST_PP_LOCAL_ITERATE()
        #undef BOOST_PROTO_ARG

            template<typename T>
            void operator()(T const &t) const
            {
                (*this)(t.proto_base());
            }

        private:
            int depth_;
            mutable bool first_;
            std::ostream &sout_;
        };
    }

    template<typename Expr>
    void display_expr(Expr const &expr)
    {
        functional::display_expr()(expr);
    }

    template<typename Expr>
    void display_expr(Expr const &expr, std::ostream &sout)
    {
        functional::display_expr(0, sout)(expr);
    }

}}

#endif
