#ifndef BOOST_PARSER_DETAIL_PRINTING_HPP
#define BOOST_PARSER_DETAIL_PRINTING_HPP

#include <boost/parser/parser_fwd.hpp>
#include <boost/parser/tuple.hpp>
#include <boost/parser/detail/detection.hpp>
#include <boost/parser/detail/hl.hpp>
#include <boost/parser/detail/text/unpack.hpp>
#if defined(_MSC_VER) && defined(BOOST_PARSER_TRACE_TO_VS_OUTPUT)
#include <boost/parser/detail/vs_output_stream.hpp>
#endif
#include <boost/parser/detail/text/transcode_view.hpp>

#include <iomanip>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <variant>

#include <cctype>


namespace boost { namespace parser { namespace detail {

    template<typename Context>
    decltype(auto) _indent(Context const & context);

    template<typename Char>
    std::ostream & print_char(std::ostream & os, Char c)
    {
        if constexpr (
#if defined(__cpp_char8_t)
            std::is_same_v<
                char8_t,
                std::remove_cv_t<std::remove_reference_t<Char>>>
#else
            false
#endif
        ) {
            os << char(c);
        } else {
            os << c;
        }
        return os;
    }

    enum { parser_component_limit = 4 };

    template<
        typename Context,
        typename Parser,
        typename DelimiterParser,
        typename MinType,
        typename MaxType>
    void print_parser(
        Context const & context,
        repeat_parser<Parser, DelimiterParser, MinType, MaxType> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        opt_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename ParserTuple>
    void print_parser(
        Context const & context,
        or_parser<ParserTuple> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename ParserTuple, typename DelimiterParser>
    void print_parser(
        Context const & context,
        perm_parser<ParserTuple, DelimiterParser> const & parser,
        std::ostream & os,
        int components = 0);

    template<
        typename Context,
        typename ParserTuple,
        typename BacktrackingTuple,
        typename CombiningGroups>
    void print_parser(
        Context const & context,
        seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
            parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser, typename Action>
    void print_parser(
        Context const & context,
        action_parser<Parser, Action> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser, typename F>
    void print_parser(
        Context const & context,
        transform_parser<Parser, F> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        omit_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        raw_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);

#if defined(BOOST_PARSER_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS
    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        string_view_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);
#endif

    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        lexeme_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser>
    void print_parser(
        Context const & context,
        no_case_parser<Parser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser, typename SkipParser>
    void print_parser(
        Context const & context,
        skip_parser<Parser, SkipParser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Parser, bool FailOnMatch>
    void print_parser(
        Context const & context,
        expect_parser<Parser, FailOnMatch> const & parser,
        std::ostream & os,
        int components = 0);

    template<
        typename Context,
        bool UseCallbacks,
        typename Parser,
        typename Attribute,
        typename LocalState,
        typename ParamsTuple>
    void print_parser(
        Context const & context,
        rule_parser<
            UseCallbacks,
            Parser,
            Attribute,
            LocalState,
            ParamsTuple> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename T>
    void print_parser(
        Context const & context,
        symbol_parser<T> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Predicate>
    void print_parser(
        Context const & context,
        eps_parser<Predicate> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        eps_parser<nope> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        eoi_parser const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Atribute>
    void print_parser(
        Context const & context,
        attr_parser<Atribute> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Expected, typename AttributeType>
    void print_parser(
        Context const & context,
        char_parser<Expected, AttributeType> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        digit_parser const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_subrange_parser<hex_digit_subranges> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_subrange_parser<control_subranges> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_set_parser<punct_chars> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_set_parser<symb_chars> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_set_parser<lower_case_chars> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        char_set_parser<upper_case_chars> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename Expected, typename AttributeType>
    void print_parser(
        Context const & context,
        omit_parser<char_parser<Expected, AttributeType>> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename StrIter, typename StrSentinel>
    void print_parser(
        Context const & context,
        string_parser<StrIter, StrSentinel> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename StrIter, typename StrSentinel>
    void print_parser(
        Context const & context,
        omit_parser<string_parser<StrIter, StrSentinel>> const & parser,
        std::ostream & os,
        int components = 0);

    template<
        typename Context,
        typename Quotes,
        typename Escapes,
        typename CharParser>
    void print_parser(
        Context const & context,
        quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, bool NewlinesOnly, bool NoNewlines>
    void print_parser(
        Context const & context,
        ws_parser<NewlinesOnly, NoNewlines> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        bool_parser const & parser,
        std::ostream & os,
        int components = 0);

    template<
        typename Context,
        typename T,
        int Radix,
        int MinDigits,
        int MaxDigits,
        typename Expected>
    void print_parser(
        Context const & context,
        uint_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
        std::ostream & os,
        int components = 0);

    template<
        typename Context,
        typename T,
        int Radix,
        int MinDigits,
        int MaxDigits,
        typename Expected>
    void print_parser(
        Context const & context,
        int_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename T>
    void print_parser(
        Context const & context,
        float_parser<T> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        float_parser<float> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context>
    void print_parser(
        Context const & context,
        float_parser<double> const & parser,
        std::ostream & os,
        int components = 0);

    template<typename Context, typename SwitchValue, typename OrParser>
    void print_parser(
        Context const & context,
        switch_parser<SwitchValue, OrParser> const & parser,
        std::ostream & os,
        int components = 0);

    enum { trace_indent_factor = 2 };

    inline void trace_indent(std::ostream & os, int indent)
    {
        for (int i = 0, end = trace_indent_factor * indent; i != end; ++i) {
            os << ' ';
        }
    }

    template<typename Iter, typename Sentinel, int SizeofValueType>
    struct trace_input_impl
    {
        static void call(
            std::ostream & os,
            Iter first_,
            Sentinel last_,
            bool quote,
            int64_t trace_input_cps)
        {
            auto utf8 = BOOST_PARSER_DETAIL_TEXT_SUBRANGE(first_, last_) | text::as_utf8;
            auto first = utf8.begin();
            auto last = utf8.end();
            if (quote)
                os << '"';
            for (int64_t i = 0; i < trace_input_cps && first != last;
                 ++i, ++first) {
                detail::print_char(os, *first);
            }
            if (quote)
                os << '"';
        }
    };

    template<typename Iter, typename Sentinel>
    struct trace_input_impl<Iter, Sentinel, 1>
    {
        static void call(
            std::ostream & os,
            Iter first_,
            Sentinel last_,
            bool quote,
            int64_t trace_input_cps)
        {
            auto r = BOOST_PARSER_DETAIL_TEXT_SUBRANGE(first_, last_);
            auto r_unpacked =
                detail::text::unpack_iterator_and_sentinel(first_, last_);
            auto utf32 = r | text::as_utf32;
            auto first = utf32.begin();
            auto const last = utf32.end();
            for (int64_t i = 0; i < trace_input_cps && first != last; ++i) {
                ++first;
            }
            if (quote)
                os << '"';
            auto first_repacked = r_unpacked.repack(first.base());
            for (Iter it = first_, end = first_repacked; it != end; ++it) {
                detail::print_char(os, *it);
            }
            if (quote)
                os << '"';
        }
    };

    template<typename Iter, typename Sentinel>
    inline void trace_input(
        std::ostream & os,
        Iter first,
        Sentinel last,
        bool quote = true,
        int64_t trace_input_cps = 8)
    {
        trace_input_impl<Iter, Sentinel, sizeof(*first)>::call(
            os, first, last, quote, trace_input_cps);
    }

    template<typename Iter, typename Sentinel>
    inline void trace_begin_match(
        std::ostream & os,
        Iter first,
        Sentinel last,
        int indent,
        std::string_view name)
    {
        detail::trace_indent(os, indent);
        os << "[begin " << name << "; input=";
        detail::trace_input(os, first, last);
        os << "]" << std::endl;
    }

    template<typename Iter, typename Sentinel>
    inline void trace_end_match(
        std::ostream & os,
        Iter first,
        Sentinel last,
        int indent,
        std::string_view name)
    {
        detail::trace_indent(os, indent);
        os << "[end " << name << "; input=";
        detail::trace_input(os, first, last);
        os << "]" << std::endl;
    }

    template<typename Iter, typename Sentinel, typename Context>
    void trace_prefix(
        std::ostream & os,
        Iter first,
        Sentinel last,
        Context const & context,
        std::string_view name)
    {
        int & indent = detail::_indent(context);
        detail::trace_begin_match(os, first, last, indent, name);
        ++indent;
    }

    template<typename Iter, typename Sentinel, typename Context>
    void trace_suffix(
        std::ostream & os,
        Iter first,
        Sentinel last,
        Context const & context,
        std::string_view name)
    {
        int & indent = detail::_indent(context);
        --indent;
        detail::trace_end_match(os, first, last, indent, name);
    }

    template<typename T>
    using streamable =
        decltype(std::declval<std::ostream &>() << std::declval<T const &>());

    template<typename T, bool Streamable = is_detected_v<streamable, T>>
    struct printer
    {
        std::ostream & operator()(std::ostream & os, T const &)
        {
            return os << "<<unprintable-value>>";
        }
    };

    template<typename T>
    void print_printable(std::ostream & os, T const & x)
    {
        os << x;
    }

    inline void print_printable(std::ostream & os, char c)
    {
        if (std::isprint(c)) {
            os << "'" << c << "'";
        } else {
            unsigned char c_ = c;
            os << "'\\x" << std::hex << std::setfill('0') << (uint32_t)c_
               << "'";
        }
    }

    inline void print_printable(std::ostream & os, char32_t c)
    {
        if (c < 256) {
            os << "U";
            detail::print_printable(os, (char)c);
        } else {
            os << "U'\\U" << std::hex << std::setfill('0') << (int32_t)c << "'";
        }
    }

    template<typename T>
    struct printer<T, true>
    {
        std::ostream & operator()(std::ostream & os, T const & x)
        {
            detail::print_printable(os, x);
            return os;
        }
    };

    template<typename T>
    constexpr bool is_variant_v = enable_variant<T>;

    template<typename Attribute>
    inline void print(std::ostream & os, Attribute const & attr)
    {
        using just_attribute =
            std::remove_cv_t<std::remove_reference_t<Attribute>>;
        if constexpr (is_tuple<just_attribute>{}) {
            os << "(";
            bool first = true;
            hl::for_each(attr, [&](auto const & a) {
                if (!first)
                    os << ", ";
                detail::print(os, a);
                first = false;
            });
            os << ")";
        } else if constexpr (is_optional_v<just_attribute>) {
            if (!attr)
                os << "<<empty>>";
            else
                detail::print(os, *attr);
        } else if constexpr (is_variant_v<just_attribute>) {
            os << "<<variant>>";
        } else {
            printer<just_attribute>{}(os, attr);
        }
    }

    template<typename Attribute>
    inline void
    print_attribute(std::ostream & os, Attribute const & attr, int indent)
    {
        detail::trace_indent(os, indent);
        os << "attribute: ";
        detail::print(os, attr);
        os << "\n";
    }

    inline void print_attribute(std::ostream &, nope const &, int) {}

    constexpr inline bool do_trace(flags f)
    {
        return (uint32_t(f) & uint32_t(flags::trace)) == uint32_t(flags::trace);
    }

    template<typename Context, typename T>
    auto resolve(Context const & context, T const & x);

    template<typename Context>
    auto resolve(Context const &, nope n);

    template<
        bool DoTrace,
        typename Iter,
        typename Sentinel,
        typename Context,
        typename Attribute>
    struct scoped_trace_t
    {
        scoped_trace_t(
            std::ostream & os,
            Iter & first,
            Sentinel last,
            Context const & context,
            flags f,
            Attribute const & attr,
            std::string name) :
            os_(os),
            initial_first_(first),
            first_(first),
            last_(last),
            context_(context),
            flags_(f),
            attr_(attr),
            name_(std::move(name))
        {
            if (!detail::do_trace(flags_))
                return;
            detail::trace_prefix(os, first_, last_, context_, name_);
        }

        ~scoped_trace_t()
        {
            if (!detail::do_trace(flags_))
                return;
            detail::trace_indent(os_, detail::_indent(context_));
            if (*context_.pass_) {
                os_ << "matched ";
                detail::trace_input(os_, initial_first_, first_);
                os_ << "\n";
                detail::print_attribute(
                    os_,
                    detail::resolve(context_, attr_),
                    detail::_indent(context_));
            } else {
                os_ << "no match\n";
            }
            detail::trace_suffix(os_, first_, last_, context_, name_);
        }

        std::ostream & os_;
        Iter initial_first_;
        Iter & first_;
        Sentinel last_;
        Context const & context_;
        flags flags_;
        Attribute const & attr_;
        std::string name_;
    };

    template<
        typename Iter,
        typename Sentinel,
        typename Context,
        typename Attribute>
    struct scoped_trace_t<false, Iter, Sentinel, Context, Attribute>
    {
        scoped_trace_t() {}
    };

    template<
        typename Parser,
        typename Iter,
        typename Sentinel,
        typename Context,
        typename Attribute>
    auto scoped_trace(
        Parser const & parser,
        Iter & first,
        Sentinel last,
        Context const & context,
        flags f,
        Attribute const & attr)
    {
        if constexpr (Context::do_trace) {
            std::stringstream oss;
            detail::print_parser(context, parser, oss);
            std::ostream & os = BOOST_PARSER_TRACE_OSTREAM;
            return scoped_trace_t<true, Iter, Sentinel, Context, Attribute>(
                os, first, last, context, f, attr, oss.str());
        } else {
            return scoped_trace_t<false, Iter, Sentinel, Context, Attribute>{};
        }
    }

    template<typename Context, typename Attribute>
    auto final_trace(Context const & context, flags f, Attribute const & attr)
    {
        if (!detail::do_trace(f))
            return;

        std::ostream & os = BOOST_PARSER_TRACE_OSTREAM;
        os << "--------------------\n";
        if (*context.pass_) {
            os << "parse succeeded\n";
            detail::print_attribute(os, detail::resolve(context, attr), 0);
        } else {
            os << "parse failed\n";
        }
        os << "--------------------" << std::endl;
    }

}}}

#endif
