/*
 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see .
 */
#ifndef TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H
#define TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H
#include 
#include 
#include 
#include 
#include 
#include 
namespace Trinity::Asio
{
/// The AsExpected class is used to indicate that any arguments to the
/// completion handler should be combined and passed as a single @c boost::outcome_v2::result argument.
/// The arguments are first moved into a @c boost::outcome_v2::result and that is then
/// passed to the completion handler.
template 
class AsExpected
{
public:
    // First dummy argument is used to prevent the "default" constructor from being used for implicit conversions
    constexpr AsExpected(std::type_identity = {}, CompletionToken token = {})
        : token_(std::forward(token))
    {
    }
    template  requires (!std::same_as)
    constexpr explicit AsExpected(T&& completion_token)
        : token_(std::forward(completion_token))
    {
    }
    /// Adapts an executor to add the @c AsExpected completion token as the
    /// default.
    template 
    struct executor_with_default : InnerExecutor
    {
        /// Specify @c AsExpected as the default completion token type.
        typedef AsExpected default_completion_token_type;
        /// Construct the adapted executor from the inner executor type.
        template 
        executor_with_default(InnerExecutor1 const& ex,
            std::enable_if_t<
                std::conditional_t<
                    !std::is_same_v,
                    std::is_convertible,
                    std::false_type
                >::value,
                int
            > = 0) noexcept
            : InnerExecutor(ex)
        {
        }
    };
    /// Type alias to adapt an I/O object to use @c AsExpected as its
    /// default completion token type.
    template 
    using as_default_on_t = typename T::template rebind_executor>::other;
    /// Function helper to adapt an I/O object to use @c AsExpected as its
    /// default completion token type.
    template 
    static auto as_default_on(T&& object)
    {
        return as_default_on_t>(static_cast(object));
    }
    CompletionToken token_;
};
struct AsExpectedFn
{
    /// Adapt a @ref completion_token to specify that the completion handler
    /// arguments should be combined into a single @c boost::outcome_v2::result argument.
    template 
    [[nodiscard]] inline constexpr AsExpected> operator()(CompletionToken&& completion_token) const
    {
        return AsExpected>(static_cast(completion_token));
    }
};
/// A function object that adapts a @ref completion_token to specify that the
/// completion handler arguments should be combined into a single @c boost::outcome_v2::result
/// argument.
///
/// May also be used directly as a completion token, in which case it adapts the
/// asynchronous operation's default completion token (or @ref boost::asio::deferred
/// if no default is available).
inline constexpr AsExpectedFn as_expected;
namespace Impl
{
template 
concept CompletionTokenError = std::same_as, boost::system::error_code>
    || std::same_as, std::exception_ptr>;
template 
class AsExpectedHandler
{
public:
    typedef void result_type;
    template 
    AsExpectedHandler(AsExpected e) : handler_(static_cast(e.token_)) { }
    template  requires (!std::same_as)
    AsExpectedHandler(RedirectedHandler&& h) : handler_(std::forward(h)) { }
    template 
    inline void operator()(Error&& e)
    {
        using return_type = boost::outcome_v2::result>;
        if (e)
            static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e))));
        else
            static_cast(handler_)(return_type(boost::outcome_v2::success()));
    }
    template 
    inline void operator()(Error&& e, Arg&& value)
    {
        using return_type = boost::outcome_v2::result, std::remove_cvref_t>;
        if (e)
            static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e))));
        else
            static_cast(handler_)(return_type(boost::outcome_v2::success(std::forward(value))));
    }
    template 
    inline void operator()(Error&& e, Arg&& first, Args&&... rest)
    {
        using return_type = boost::outcome_v2::result, std::decay_t...>, std::remove_cvref_t>;
        if (e)
            static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e))));
        else
            static_cast(handler_)(return_type(boost::outcome_v2::success(std::make_tuple(std::forward(first), std::forward(rest)...))));
    }
    Handler handler_;
};
template 
inline bool asio_handler_is_continuation(AsExpectedHandler* this_handler)
{
    return boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
}
template 
struct AsExpectedSignature;
#define STAMP_AS_EXPECTED_SIGNATURE(qualifier) \
    template  \
    struct AsExpectedSignature \
    { \
        using type = R(boost::outcome_v2::result>) qualifier; \
    }; \
    template  \
    struct AsExpectedSignature \
    { \
        using type = R(boost::outcome_v2::result, std::remove_cvref_t>) qualifier; \
    }; \
    template  \
    struct AsExpectedSignature \
    { \
        using type = R(boost::outcome_v2::result, std::decay_t...>, std::remove_cvref_t>) qualifier; \
    };
STAMP_AS_EXPECTED_SIGNATURE(BOOST_PP_EMPTY());
STAMP_AS_EXPECTED_SIGNATURE(&);
STAMP_AS_EXPECTED_SIGNATURE(&&);
STAMP_AS_EXPECTED_SIGNATURE(noexcept);
STAMP_AS_EXPECTED_SIGNATURE(& noexcept);
STAMP_AS_EXPECTED_SIGNATURE(&& noexcept);
} // namespace Impl
}
namespace boost::asio
{
#if BOOST_VERSION >= 107700
template 
class async_result, Signatures...> : async_result::type...>
{
    template 
    struct init_wrapper
    {
        explicit init_wrapper(Initiation const& initiation) : initiation_(initiation) { }
        explicit init_wrapper(Initiation&& initiation) : initiation_(std::move(initiation)) { }
        template 
        inline void operator()(Handler&& handler, Args&&... args) &&
        {
            static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...);
        }
        template 
        inline void operator()(Handler&& handler, Args&&... args) const &
        {
            static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...);
        }
        Initiation initiation_;
    };
public:
    template 
    static inline auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
    {
        return async_initiate<
            conditional_t<
            is_const>::value,
            CompletionToken const, CompletionToken>,
            typename Trinity::Asio::Impl::AsExpectedSignature::type...>(
                init_wrapper>(
                    std::forward(initiation)),
                token.token_, std::forward(args)...);
    }
};
template  class Associator, typename Handler, typename DefaultCandidate, typename _>
struct associator;
template  class Associator, typename Handler, typename DefaultCandidate>
struct associator, DefaultCandidate, void> : Associator
{
    static inline auto get(Trinity::Asio::Impl::AsExpectedHandler const& h) noexcept
    {
        return Associator::get(h.handler_);
    }
    static inline auto get(Trinity::Asio::Impl::AsExpectedHandler const& h, DefaultCandidate const& c) noexcept
    {
        return Associator::get(h.handler_, c);
    }
};
template 
class async_result
{
public:
    template 
    static inline auto initiate(Initiation&& initiation, RawCompletionToken&&, Args&&... args)
    {
        return async_initiate(
            std::forward(initiation),
            Trinity::Asio::AsExpected<
            default_completion_token_t>>{},
            std::forward(args)...);
    }
};
#else
template 
class async_result, Signature> : async_result::type>
{
    template 
    struct init_wrapper
    {
        explicit init_wrapper(Initiation const& initiation) : initiation_(initiation) { }
        explicit init_wrapper(Initiation&& initiation) : initiation_(std::move(initiation)) { }
        template 
        inline void operator()(Handler&& handler, Args&&... args) &&
        {
            static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...);
        }
        template 
        inline void operator()(Handler&& handler, Args&&... args) const &
        {
            static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...);
        }
        Initiation initiation_;
    };
public:
    template 
    static inline auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
    {
        return async_initiate<
            conditional_t<
            is_const>::value,
            CompletionToken const, CompletionToken>,
            typename Trinity::Asio::Impl::AsExpectedSignature::type>(
                init_wrapper>(
                    std::forward(initiation)),
                token.token_, std::forward(args)...);
    }
};
#endif
} // namespace boost::asio
#endif // TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H