#ifndef BOOST_PARSER_REPLACE_HPP
#define BOOST_PARSER_REPLACE_HPP

#include <boost/parser/search.hpp>

#if !defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS


namespace boost::parser {

    namespace detail {
        template<typename T, bool = std::is_pointer_v<remove_cv_ref_t<T>>>
        constexpr auto range_value_type =
            wrapper<remove_cv_ref_t<range_value_t<T>>>{};
        template<typename T>
        constexpr auto range_value_type<T, true> = wrapper<
            remove_cv_ref_t<std::remove_pointer_t<remove_cv_ref_t<T>>>>{};

        template<typename T>
        constexpr text::format range_utf_format()
        {
#if !BOOST_PARSER_USE_CONCEPTS
            // Special case: the metafunctions above will not detect char8_t
            // in C++17 mode, since it does not exit yet!  So, we need to
            // detect utf8_view in particular, and know that its use implies
            // format::utf8.
            if constexpr (is_utf8_view<T>{}) {
                return format::utf8;
            } else {
#endif
                using value_t = typename decltype(range_value_type<T>)::type;
                if constexpr (std::is_same_v<value_t, char>) {
                    return no_format;
#if defined(__cpp_char8_t)
                } else if constexpr (std::is_same_v<value_t, char8_t>) {
                    return format::utf8;
#endif
                } else if constexpr (
                    std::is_same_v<value_t, char16_t>
#ifdef _MSC_VER
                    || std::is_same_v<T, wchar_t>
#endif
                ) {
                    return format::utf16;
                } else if constexpr (
                    std::is_same_v<value_t, char32_t>
#ifndef _MSC_VER
                    || std::is_same_v<T, wchar_t>
#endif
                ) {
                    return format::utf32;
                } else {
                    static_assert(
                        sizeof(T) && false,
                        "Looks like you're trying to pass a range to replace "
                        "or transform_replace that has a non-character type "
                        "for its value type.  This is not supported.");
                }
#if !BOOST_PARSER_USE_CONCEPTS
            }
#endif
        }

        template<typename T>
        constexpr text::format
            range_utf_format_v = detail::range_utf_format<remove_cv_ref_t<T>>();

        template<typename V1, typename V2>
        using concat_reference_t =
            std::common_type_t<range_reference_t<V1>, range_reference_t<V2>>;
        template<typename V1, typename V2>
        using concat_value_t =
            std::common_type_t<range_value_t<V1>, range_value_t<V2>>;
        template<typename V1, typename V2>
        using concat_rvalue_reference_t = std::common_type_t<
            range_rvalue_reference_t<V1>,
            range_rvalue_reference_t<V2>>;

#if BOOST_PARSER_USE_CONCEPTS
        // clang-format off
        template<typename ReplacementV, typename V>
        concept concatable = requires {
            typename detail::concat_reference_t<ReplacementV, V>;
            typename detail::concat_value_t<ReplacementV, V>;
            typename detail::concat_rvalue_reference_t<ReplacementV, V>;
        };
        // clang-format on
#else
        template<typename ReplacementV, typename V>
        // clang-format off
        using concatable_expr = decltype(
            std::declval<concat_reference_t<ReplacementV, V>>(),
            std::declval<concat_value_t<ReplacementV, V>>(),
            std::declval<concat_rvalue_reference_t<ReplacementV, V>>());
        // clang-format on
        template<typename ReplacementV, typename V>
        constexpr bool concatable =
            is_detected_v<concatable_expr, ReplacementV, V>;
#endif

        template<
            typename V1,
            typename V2
#if !BOOST_PARSER_USE_CONCEPTS
            ,
            typename Enable = std::enable_if_t<concatable<V1, V2>>
#endif
            >
#if BOOST_PARSER_USE_CONCEPTS
        requires concatable<V1, V2>
#endif
        struct either_iterator_impl
            : detail::stl_interfaces::iterator_interface<
                  either_iterator_impl<V1, V2>,
                  std::forward_iterator_tag,
                  concat_value_t<V1, V2>,
                  concat_reference_t<V1, V2>>
        {
            constexpr either_iterator_impl() = default;
            constexpr either_iterator_impl(iterator_t<V1> it) : it_(it) {}
            template<typename V = V2>
            constexpr either_iterator_impl(iterator_t<V> it) : it_(it)
            {}

            constexpr concat_reference_t<V1, V2> operator*() const
            {
                if (it_.index() == 0) {
                    return *std::get<0>(it_);
                } else {
                    return *std::get<1>(it_);
                }
            }

            constexpr either_iterator_impl & operator++()
            {
                if (it_.index() == 0)
                    ++std::get<0>(it_);
                else
                    ++std::get<1>(it_);
                return *this;
            }

            friend constexpr bool
            operator==(either_iterator_impl lhs, either_iterator_impl rhs)
            {
                if (lhs.it_.index() != rhs.it_.index())
                    return false;
                if (lhs.it_.index() == 0)
                    return std::get<0>(lhs.it_) == std::get<0>(rhs.it_);
                else
                    return std::get<1>(lhs.it_) == std::get<1>(rhs.it_);
            }

            using base_type = detail::stl_interfaces::iterator_interface<
                either_iterator_impl<V1, V2>,
                std::forward_iterator_tag,
                concat_value_t<V1, V2>,
                concat_reference_t<V1, V2>>;
            using base_type::operator++;

        private:
            std::variant<iterator_t<V1>, iterator_t<V2>> it_;
        };

        template<typename V1, typename V2>
        using either_iterator = std::conditional_t<
            std::is_same_v<iterator_t<V1>, iterator_t<V2>>,
            iterator_t<V1>,
            either_iterator_impl<V1, V2>>;

#if BOOST_PARSER_USE_CONCEPTS
        // clang-format off
        template<typename ReplacementV, typename V>
        concept replacement_for = requires (ReplacementV replacement, V base) {
            { either_iterator<V, ReplacementV>(replacement.begin()) };
            { either_iterator<V, ReplacementV>(replacement.end()) };
            { either_iterator<V, ReplacementV>(base.begin()) };
        };
        // clang-format on
#else
        template<typename ReplacementV, typename V>
        using replacement_for_expr = decltype(
            either_iterator<V, ReplacementV>(
                std::declval<ReplacementV&>().begin()),
            either_iterator<V, ReplacementV>(
                std::declval<ReplacementV&>().end()),
            either_iterator<V, ReplacementV>(std::declval<V&>().begin()));
        template<typename ReplacementV, typename V>
        constexpr bool replacement_for =
            is_detected_v<replacement_for_expr, ReplacementV, V>;
#endif
    }

    /** Produces a range of subranges of a given range `base`.  Each subrange
        is either a subrange of `base` that does not match the given parser
        `parser`, or is the given replacement for a match, `replacement`.

        In addition to the template parameter constraints, `V` and
        `ReplacementV` must be ranges of `char`, or must have the same UTF
        format, and `V` and `ReplacementV` must meet the same compatibility
        requirements as described in `std::ranges::join_view`. */
    template<
#if BOOST_PARSER_USE_CONCEPTS
        std::ranges::viewable_range V,
        std::ranges::viewable_range ReplacementV,
#else
        typename V,
        typename ReplacementV,
#endif
        typename Parser,
        typename GlobalState,
        typename ErrorHandler,
        typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
        ,
        typename Enable = std::enable_if_t<
            detail::replacement_for<ReplacementV, V> &&
            (detail::range_utf_format_v<V> ==
             detail::range_utf_format_v<ReplacementV>)>
#endif
        >
#if BOOST_PARSER_USE_CONCEPTS
    requires detail::replacement_for<ReplacementV, V> &&
        (detail::range_utf_format_v<V> ==
         detail::range_utf_format_v<ReplacementV>)
#endif
            struct replace_view
        : detail::stl_interfaces::view_interface<replace_view<
              V,
              ReplacementV,
              Parser,
              GlobalState,
              ErrorHandler,
              SkipParser>>
    {
        constexpr replace_view() = default;
        constexpr replace_view(
            V base,
            parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
            parser_interface<SkipParser> const & skip,
            ReplacementV replacement,
            trace trace_mode = trace::off) :
            base_(std::move(base)),
            replacement_(std::move(replacement)),
            parser_(parser),
            skip_(skip),
            trace_mode_(trace_mode)
        {}
        constexpr replace_view(
            V base,
            parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
            ReplacementV replacement,
            trace trace_mode = trace::off) :
            base_(std::move(base)),
            replacement_(std::move(replacement)),
            parser_(parser),
            skip_(),
            trace_mode_(trace_mode)
        {}

        constexpr V base() const &
#if BOOST_PARSER_USE_CONCEPTS
            requires std::copy_constructible<V>
#endif
        {
            return base_;
        }
        constexpr V base() && { return std::move(base_); }

        constexpr V replacement() const &
#if BOOST_PARSER_USE_CONCEPTS
            requires std::copy_constructible<V>
#endif
        {
            return replacement_;
        }
        constexpr V replacement() && { return std::move(replacement_); }

        constexpr auto begin() { return iterator<false>{this}; }
        constexpr auto end() { return sentinel<false>{}; }

        constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
            requires std::ranges::range<const V>
#endif
        {
            return iterator<true>{this};
        }
        constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
            requires std::ranges::range<const V>
#endif
        {
            return sentinel<true>{};
        }

        template<bool Const>
        struct sentinel
        {};

        template<bool Const>
        struct iterator : detail::stl_interfaces::proxy_iterator_interface<
                              iterator<Const>,
                              std::forward_iterator_tag,
                              BOOST_PARSER_SUBRANGE<detail::either_iterator<
                                  detail::maybe_const<Const, V>,
                                  detail::maybe_const<Const, ReplacementV>>>>
        {
            using I = detail::iterator_t<detail::maybe_const<Const, V>>;
            using S = detail::sentinel_t<detail::maybe_const<Const, V>>;

            using ref_t_iter = detail::either_iterator<
                detail::maybe_const<Const, V>,
                detail::maybe_const<Const, ReplacementV>>;
            using reference_type = BOOST_PARSER_SUBRANGE<ref_t_iter>;

            constexpr iterator() = default;
            constexpr iterator(
                detail::maybe_const<Const, replace_view> * parent) :
                parent_(parent),
                r_(parent_->base_.begin(), parent_->base_.end()),
                curr_(r_.begin(), r_.begin()),
                next_it_(r_.begin()),
                in_match_(true)
            {
                ++*this;
            }

            constexpr iterator & operator++()
            {
                if (in_match_) {
                    r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
                    auto const new_match = parser::search(
                        r_,
                        parent_->parser_,
                        parent_->skip_,
                        parent_->trace_mode_);
                    if (new_match.begin() == curr_.end()) {
                        curr_ = new_match;
                    } else {
                        curr_ =
                            BOOST_PARSER_SUBRANGE(next_it_, new_match.begin());
                        in_match_ = false;
                    }
                    next_it_ = new_match.end();
                } else {
                    if (!curr_.empty()) {
                        curr_ = BOOST_PARSER_SUBRANGE(curr_.end(), next_it_);
                        in_match_ = true;
                    }
                    if (curr_.empty())
                        r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
                }
                return *this;
            }

            constexpr reference_type operator*() const
            {
                if (in_match_) {
                    return reference_type(
                        ref_t_iter(parent_->replacement_.begin()),
                        ref_t_iter(parent_->replacement_.end()));
                } else {
                    return reference_type(
                        ref_t_iter(curr_.begin()), ref_t_iter(curr_.end()));
                }
            }

            friend constexpr bool operator==(iterator lhs, iterator rhs)
            {
                return lhs.r_.begin() == rhs.r_.begin();
            }
            friend constexpr bool operator==(iterator it, sentinel<Const>)
            {
                return it.r_.begin() == it.r_.end();
            }

            using base_type = detail::stl_interfaces::proxy_iterator_interface<
                iterator,
                std::forward_iterator_tag,
                reference_type>;
            using base_type::operator++;

        private:
            detail::maybe_const<Const, replace_view> * parent_;
            BOOST_PARSER_SUBRANGE<I, S> r_;
            BOOST_PARSER_SUBRANGE<I> curr_;
            I next_it_;
            bool in_match_;
        };

        template<bool Const>
        friend struct iterator;

    private:
        V base_;
        ReplacementV replacement_;
        parser_interface<Parser, GlobalState, ErrorHandler> parser_;
        parser_interface<SkipParser> skip_;
        trace trace_mode_;
    };

    // deduction guides
    template<
        typename V,
        typename ReplacementV,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler,
        typename SkipParser>
    replace_view(
        V &&,
        parser_interface<Parser, GlobalState, ErrorHandler>,
        parser_interface<SkipParser>,
        ReplacementV &&,
        trace)
        -> replace_view<
            detail::text::detail::all_t<V>,
            detail::text::detail::all_t<ReplacementV>,
            Parser,
            GlobalState,
            ErrorHandler,
            SkipParser>;

    template<
        typename V,
        typename ReplacementV,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler,
        typename SkipParser>
    replace_view(
        V &&,
        parser_interface<Parser, GlobalState, ErrorHandler>,
        parser_interface<SkipParser>,
        ReplacementV &&)
        -> replace_view<
            detail::text::detail::all_t<V>,
            detail::text::detail::all_t<ReplacementV>,
            Parser,
            GlobalState,
            ErrorHandler,
            SkipParser>;

    template<
        typename V,
        typename ReplacementV,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler>
    replace_view(
        V &&,
        parser_interface<Parser, GlobalState, ErrorHandler>,
        ReplacementV &&,
        trace)
        -> replace_view<
            detail::text::detail::all_t<V>,
            detail::text::detail::all_t<ReplacementV>,
            Parser,
            GlobalState,
            ErrorHandler,
            parser_interface<eps_parser<detail::phony>>>;

    template<
        typename V,
        typename ReplacementV,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler>
    replace_view(
        V &&,
        parser_interface<Parser, GlobalState, ErrorHandler>,
        ReplacementV &&)
        -> replace_view<
            detail::text::detail::all_t<V>,
            detail::text::detail::all_t<ReplacementV>,
            Parser,
            GlobalState,
            ErrorHandler,
            parser_interface<eps_parser<detail::phony>>>;

    namespace detail {
        template<
            typename V,
            typename ReplacementV,
            typename Parser,
            typename GlobalState,
            typename ErrorHandler,
            typename SkipParser>
        using replace_view_expr = decltype(replace_view<
                                           V,
                                           ReplacementV,
                                           Parser,
                                           GlobalState,
                                           ErrorHandler,
                                           SkipParser>(
            std::declval<V>(),
            std::declval<
                parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
            std::declval<parser_interface<SkipParser> const &>(),
            std::declval<ReplacementV>(),
            trace::on));

        template<
            typename V,
            typename ReplacementV,
            typename Parser,
            typename GlobalState,
            typename ErrorHandler,
            typename SkipParser>
        constexpr bool can_replace_view = is_detected_v<
            replace_view_expr,
            V,
            ReplacementV,
            Parser,
            GlobalState,
            ErrorHandler,
            SkipParser>;

        struct replace_impl
        {
#if BOOST_PARSER_USE_CONCEPTS

            template<
                parsable_range R,
                std::ranges::range ReplacementR,
                typename Parser,
                typename GlobalState,
                typename ErrorHandler,
                typename SkipParser>
            requires
                // clang-format off
                std::ranges::viewable_range<R> &&
                std::ranges::viewable_range<ReplacementR> &&
                // clang-format on
                can_replace_view<
                    to_range_t<R>,
                    decltype(to_range<
                             ReplacementR,
                             true,
                             detail::range_utf_format_v<R>>::
                                 call(std::declval<ReplacementR>())),
                    Parser,
                    GlobalState,
                    ErrorHandler,
                    SkipParser>
                // clang-format off
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                parser_interface<SkipParser> const & skip,
                ReplacementR && replacement,
                trace trace_mode = trace::off) const
            // clang-format on
            {
                return replace_view(
                    to_range<R>::call((R &&) r),
                    parser,
                    skip,
                    to_range<
                        ReplacementR,
                        true,
                        detail::range_utf_format_v<R>>::call((ReplacementR &&)
                                                                 replacement),
                    trace_mode);
            }

            template<
                parsable_range R,
                std::ranges::range ReplacementR,
                typename Parser,
                typename GlobalState,
                typename ErrorHandler>
            requires
                // clang-format off
                std::ranges::viewable_range<R> &&
                std::ranges::viewable_range<ReplacementR> &&
                // clang-format on
                can_replace_view<
                    to_range_t<R>,
                    decltype(to_range<
                             ReplacementR,
                             true,
                             detail::range_utf_format_v<R>>::
                                 call(std::declval<ReplacementR>())),
                    Parser,
                    GlobalState,
                    ErrorHandler,
                    parser_interface<eps_parser<detail::phony>>>
                // clang-format off
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                ReplacementR && replacement,
                trace trace_mode = trace::off) const
            // clang-format on
            {
                return (*this)(
                    (R &&) r,
                    parser,
                    parser_interface<eps_parser<detail::phony>>{},
                    (ReplacementR &&) replacement,
                    trace_mode);
            }

#else

            template<
                typename R,
                typename Parser,
                typename GlobalState,
                typename ErrorHandler,
                typename SkipParser,
                typename ReplacementR = trace,
                typename Trace = trace,
                typename Enable = std::enable_if_t<is_parsable_range_v<R>>>
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                SkipParser && skip,
                ReplacementR && replacement = ReplacementR{},
                Trace trace_mode = Trace{}) const
            {
                if constexpr (
                    is_parser_iface<remove_cv_ref_t<SkipParser>> &&
                    is_range<remove_cv_ref_t<ReplacementR>> &&
                    std::is_same_v<Trace, trace>) {
                    // (r, parser, skip, replacement, trace) case
                    return impl(
                        (R &&) r,
                        parser,
                        skip,
                        (ReplacementR &&) replacement,
                        trace_mode);
                } else if constexpr (
                    is_range<remove_cv_ref_t<SkipParser>> &&
                    std::is_same_v<remove_cv_ref_t<ReplacementR>, trace> &&
                    std::is_same_v<Trace, trace>) {
                    // (r, parser, replacement, trace) case
                    return impl(
                        (R &&) r,
                        parser,
                        parser_interface<eps_parser<detail::phony>>{},
                        (SkipParser &&) skip,
                        replacement);
                } else {
                    static_assert(
                        sizeof(R) == 1 && false,
                        "Only the signatures replace(R, parser, skip, "
                        "replcement trace = trace::off) and replace(R, parser, "
                        "replacement, trace = trace::off) are supported.");
                }
            }

        private:
            template<
                typename R,
                typename ReplacementR,
                typename Parser,
                typename GlobalState,
                typename ErrorHandler,
                typename SkipParser>
            [[nodiscard]] constexpr auto impl(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                parser_interface<SkipParser> const & skip,
                ReplacementR && replacement,
                trace trace_mode = trace::off) const
            {
                return replace_view(
                    to_range<R>::call((R &&) r),
                    parser,
                    skip,
                    to_range<
                        ReplacementR,
                        true,
                        detail::range_utf_format_v<R>>::call((ReplacementR &&)
                                                                 replacement),
                    trace_mode);
            }

#endif
        };
    }

    /** A range adaptor object ([range.adaptor.object]).  Given subexpressions
        `E` and `P`, `Q`, `R`, and 'S', each of the expressions `replace(E,
        P)`, `replace(E, P, Q)`. `replace(E, P, Q, R)`, and `replace(E, P, Q,
        R, S)` are expression-equivalent to `replace_view(E, P)`,
        `replace_view(E, P, Q)`, `replace_view(E, P, Q, R)`, `replace_view(E,
        P, Q, R, S)`, respectively. */
    inline constexpr detail::stl_interfaces::adaptor<detail::replace_impl>
        replace = detail::replace_impl{};

}

#if BOOST_PARSER_USE_CONCEPTS
template<
    typename V,
    typename ReplacementV,
    typename Parser,
    typename GlobalState,
    typename ErrorHandler,
    typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<boost::parser::replace_view<
    V,
    ReplacementV,
    Parser,
    GlobalState,
    ErrorHandler,
    SkipParser>> = std::ranges::enable_borrowed_range<V> &&
    std::ranges::enable_borrowed_range<ReplacementV>;
#endif

#endif

#endif
