//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// 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)
//
// Official repository: https://github.com/boostorg/json
//

#ifndef BOOST_JSON_DETAIL_PARSE_INTO_HPP
#define BOOST_JSON_DETAIL_PARSE_INTO_HPP

#include <boost/json/detail/config.hpp>

#include <boost/json/error.hpp>
#include <boost/json/conversion.hpp>
#include <boost/describe/enum_from_string.hpp>

#include <vector>

/*
 * This file contains the majority of parse_into functionality, specifically
 * the implementation of dedicated handlers for different generic categories of
 * types.
 *
 * At the core of parse_into is the specialisation basic_parser<
 * detail::into_handler<T> >. detail::into_handler<T> is a handler for
 * basic_parser. It directly handles events on_comment_part and on_comment (by
 * ignoring them), on_document_begin (by enabling the nested dedicated
 * handler), and on_document_end (by disabling the nested handler).
 *
 * Every other event is handled by the nested handler, which has the type
 * get_handler< T, into_handler<T> >. The second parameter is the parent
 * handler (in this case, it's the top handler, into_handler<T>). The type is
 * actually an alias to class template converting_handler, which has a separate
 * specialisation for every conversion category from the list of generic
 * conversion categories (e.g. sequence_conversion_tag, tuple_conversion_tag,
 * etc.) Instantiations of the template store a pointer to the parent handler
 * and a pointer to the value T.
 *
 * The nested handler handles specific parser events by setting error_code to
 * an appropriate value, if it receives an event it isn't supposed to handle
 * (e.g. a number handler getting an on_string event), and also updates the
 * value when appropriate. Note that they never need to handle on_comment_part,
 * on_comment, on_document_begin, and on_document_end events, as those are
 * always handled by the top handler into_handler<T>.
 *
 * When the nested handler receives an event that completes the current value,
 * it is supposed to call its parent's signal_value member function. This is
 * necessary for correct handling of composite types (e.g. sequences).
 *
 * Finally, nested handlers should always call parent's signal_end member
 * function if they don't handle on_array_end themselves. This is necessary
 * to correctly handle nested composites (e.g. sequences inside sequences).
 * signal_end can return false and set error state when the containing parser
 * requires more elements.
 *
 * converting_handler instantiations for composite categories of types have
 * their own nested handlers, to which they themselves delegate events. For
 * complex types you will get a tree of handlers with into_handler<T> as the
 * root and handlers for scalars as leaves.
 *
 * To reiterate, only into_handler has to handle on_comment_part, on_comment,
 * on_document_begin, and on_document_end; only handlers for composites and
 * into_handler has to provide signal_value and signal_end; all handlers
 * except for into_handler have to call their parent's signal_end from
 * their on_array_begin, if they don't handle it themselves; once a handler
 * receives an event that finishes its current value, it should call its
 * parent's signal_value.
 */

namespace boost {
namespace json {
namespace detail {

template< class Impl, class T, class Parent >
class converting_handler;

// get_handler
template< class V, class P >
using get_handler = converting_handler< generic_conversion_category<V>, V, P >;

template<error E> class handler_error_base
{
public:

    handler_error_base() = default;

    handler_error_base( handler_error_base const& ) = delete;
    handler_error_base& operator=( handler_error_base const& ) = delete;

public:

    bool on_object_begin( system::error_code& ec ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_array_begin( system::error_code& ec ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_array_end( system::error_code& ec ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_string_part( system::error_code& ec, string_view ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_string( system::error_code& ec, string_view ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_number_part( system::error_code& ec ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_int64( system::error_code& ec, std::int64_t ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_uint64( system::error_code& ec, std::uint64_t ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_double( system::error_code& ec, double ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_bool( system::error_code& ec, bool ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_null( system::error_code& ec ) { BOOST_JSON_FAIL( ec, E ); return false; }

    // LCOV_EXCL_START
    // parses that can't handle this would fail at on_object_begin
    bool on_object_end( system::error_code& ) { BOOST_ASSERT( false ); return false; }
    bool on_key_part( system::error_code& ec, string_view ) { BOOST_JSON_FAIL( ec, E ); return false; }
    bool on_key( system::error_code& ec, string_view ) { BOOST_JSON_FAIL( ec, E ); return false; }
    // LCOV_EXCL_STOP
};

template< class P, error E >
class scalar_handler
    : public handler_error_base<E>
{
protected:
    P* parent_;

public:
    scalar_handler(scalar_handler const&) = delete;
    scalar_handler& operator=(scalar_handler const&) = delete;

    scalar_handler(P* p): parent_( p )
    {}

    bool on_array_end( system::error_code& ec )
    {
        return parent_->signal_end(ec);
    }
};

template< class D, class V, class P, error E >
class composite_handler
{
protected:
    using inner_handler_type = get_handler<V, D>;

    P* parent_;
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic push
//# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
    V next_value_ = {};
    inner_handler_type inner_;
    bool inner_active_ = false;

public:
    composite_handler( composite_handler const& ) = delete;
    composite_handler& operator=( composite_handler const& ) = delete;

    composite_handler( P* p )
        : parent_(p), inner_( &next_value_, static_cast<D*>(this) )
    {}
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic pop
#endif

    bool signal_end(system::error_code& ec)
    {
        inner_active_ = false;
        return parent_->signal_value(ec);
    }

#define BOOST_JSON_INVOKE_INNER(f) \
    if( !inner_active_ ) { \
        BOOST_JSON_FAIL(ec, E); \
        return false; \
    } \
    else \
        return inner_.f

    bool on_object_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_begin(ec) );
    }

    bool on_object_end( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_end(ec) );
    }

    bool on_array_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_begin(ec) );
    }

    bool on_array_end( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_end(ec) );
    }

    bool on_key_part( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_key_part(ec, sv) );
    }

    bool on_key( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_key(ec, sv) );
    }

    bool on_string_part( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string_part(ec, sv) );
    }

    bool on_string( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string(ec, sv) );
    }

    bool on_number_part( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_number_part(ec) );
    }

    bool on_int64( system::error_code& ec, std::int64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_int64(ec, v) );
    }

    bool on_uint64( system::error_code& ec, std::uint64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_uint64(ec, v) );
    }

    bool on_double( system::error_code& ec, double v )
    {
        BOOST_JSON_INVOKE_INNER( on_double(ec, v) );
    }

    bool on_bool( system::error_code& ec, bool v )
    {
        BOOST_JSON_INVOKE_INNER( on_bool(ec, v) );
    }

    bool on_null( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_null(ec) );
    }

#undef BOOST_JSON_INVOKE_INNER
};

// integral handler
template<class V,
typename std::enable_if<std::is_signed<V>::value, int>::type = 0>
bool integral_in_range( std::int64_t v )
{
    return v >= (std::numeric_limits<V>::min)() && v <= (std::numeric_limits<V>::max)();
}

template<class V,
typename std::enable_if<!std::is_signed<V>::value, int>::type = 0>
bool integral_in_range( std::int64_t v )
{
    return v >= 0 && static_cast<std::uint64_t>( v ) <= (std::numeric_limits<V>::max)();
}

template<class V>
bool integral_in_range( std::uint64_t v )
{
    return v <= static_cast<typename std::make_unsigned<V>::type>( (std::numeric_limits<V>::max)() );
}

template< class V, class P >
class converting_handler<integral_conversion_tag, V, P>
    : public scalar_handler<P, error::not_integer>
{
private:
    V* value_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_number_part( system::error_code& )
    {
        return true;
    }

    bool on_int64(system::error_code& ec, std::int64_t v)
    {
        if( !integral_in_range<V>( v ) )
        {
            BOOST_JSON_FAIL( ec, error::not_exact );
            return false;
        }

        *value_ = static_cast<V>( v );
        return this->parent_->signal_value(ec);
    }

    bool on_uint64(system::error_code& ec, std::uint64_t v)
    {
        if( !integral_in_range<V>(v) )
        {
            BOOST_JSON_FAIL( ec, error::not_exact );
            return false;
        }

        *value_ = static_cast<V>(v);
        return this->parent_->signal_value(ec);
    }
};

// floating point handler
template< class V, class P>
class converting_handler<floating_point_conversion_tag, V, P>
    : public scalar_handler<P, error::not_double>
{
private:
    V* value_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_number_part( system::error_code& )
    {
        return true;
    }

    bool on_int64(system::error_code& ec, std::int64_t v)
    {
        *value_ = static_cast<V>(v);
        return this->parent_->signal_value(ec);
    }

    bool on_uint64(system::error_code& ec, std::uint64_t v)
    {
        *value_ = static_cast<V>(v);
        return this->parent_->signal_value(ec);
    }

    bool on_double(system::error_code& ec, double v)
    {
        *value_ = static_cast<V>(v);
        return this->parent_->signal_value(ec);
    }
};

// string handler
template< class V, class P >
class converting_handler<string_like_conversion_tag, V, P>
    : public scalar_handler<P, error::not_string>
{
private:
    V* value_;
    bool cleared_ = false;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_string_part( system::error_code&, string_view sv )
    {
        if( !cleared_ )
        {
            cleared_ = true;
            value_->clear();
        }

        value_->append( sv.begin(), sv.end() );
        return true;
    }

    bool on_string(system::error_code& ec, string_view sv)
    {
        if( !cleared_ )
            value_->clear();
        else
            cleared_ = false;

        value_->append( sv.begin(), sv.end() );
        return this->parent_->signal_value(ec);
    }
};

// bool handler
template< class V, class P >
class converting_handler<bool_conversion_tag, V, P>
    : public scalar_handler<P, error::not_bool>
{
private:
    V* value_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_bool(system::error_code& ec, bool v)
    {
        *value_ = v;
        return this->parent_->signal_value(ec);
    }
};

// null handler
template< class V, class P >
class converting_handler<null_like_conversion_tag, V, P>
    : public scalar_handler<P, error::not_null>
{
private:
    V* value_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_null(system::error_code& ec)
    {
        *value_ = {};
        return this->parent_->signal_value(ec);
    }
};

// described enum handler
template< class V, class P >
class converting_handler<described_enum_conversion_tag, V, P>
    : public scalar_handler<P, error::not_string>
{
#ifndef BOOST_DESCRIBE_CXX14

    static_assert(
        sizeof(V) == 0, "Enum support for parse_into requires C++14" );

#else

private:
    V* value_;
    std::string name_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_string_part( system::error_code&, string_view sv )
    {
        name_.append( sv.begin(), sv.end() );
        return true;
    }

    bool on_string(system::error_code& ec, string_view sv)
    {
        string_view name = sv;
        if( !name_.empty() )
        {
            name_.append( sv.begin(), sv.end() );
            name = name_;
        }

        if( !describe::enum_from_string(name, *value_) )
        {
            BOOST_JSON_FAIL(ec, error::unknown_name);
            return false;
        }

        return this->parent_->signal_value(ec);
    }

#endif // BOOST_DESCRIBE_CXX14
};

template< class V, class P >
class converting_handler<no_conversion_tag, V, P>
{
    static_assert( sizeof(V) == 0, "This type is not supported" );
};

// sequence handler
template< class It >
bool cannot_insert(It i, It e)
{
    return i == e;
}

template< class It1, class It2 >
std::false_type cannot_insert(It1, It2)
{
    return {};
}

template< class It >
bool needs_more_elements(It i, It e)
{
    return i != e;
}

template< class It1, class It2 >
std::false_type needs_more_elements(It1, It2)
{
    return {};
}

template<class T>
void
clear_container(
    T&,
    mp11::mp_int<2>)
{
}

template<class T>
void
clear_container(
    T& target,
    mp11::mp_int<1>)
{
    target.clear();
}

template<class T>
void
clear_container(
    T& target,
    mp11::mp_int<0>)
{
    target.clear();
}

template< class V, class P >
class converting_handler<sequence_conversion_tag, V, P>
    : public composite_handler<
        converting_handler<sequence_conversion_tag, V, P>,
        detail::value_type<V>,
        P,
        error::not_array>
{
private:
    V* value_;

    using Inserter = decltype(
        detail::inserter(*value_, inserter_implementation<V>()) );
    Inserter inserter;

public:
    converting_handler( V* v, P* p )
        : converting_handler::composite_handler(p)
        , value_(v)
        , inserter( detail::inserter(*value_, inserter_implementation<V>()) )
    {}

    bool signal_value(system::error_code& ec)
    {
        if(cannot_insert( inserter, value_->end() ))
        {
            BOOST_JSON_FAIL( ec, error::size_mismatch );
            return false;
        }

        *inserter++ = std::move(this->next_value_);
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic push
//# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
        this->next_value_ = {};
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
        return true;
    }

    bool signal_end(system::error_code& ec)
    {
        if(needs_more_elements( inserter, value_->end() ))
        {
            BOOST_JSON_FAIL( ec, error::size_mismatch );
            return false;
        }

        inserter = detail::inserter(*value_, inserter_implementation<V>());

        return converting_handler::composite_handler::signal_end(ec);
    }

    bool on_array_begin( system::error_code& ec )
    {
        if( this->inner_active_ )
            return this->inner_.on_array_begin( ec );

        this->inner_active_ = true;
        clear_container( *value_, inserter_implementation<V>() );
        return true;
    }

    bool on_array_end( system::error_code& ec )
    {
        if( this->inner_active_ )
            return this->inner_.on_array_end( ec );

        return this->parent_->signal_end(ec);
    }
};

// map handler
template< class V, class P >
class converting_handler<map_like_conversion_tag, V, P>
    : public composite_handler<
        converting_handler<map_like_conversion_tag, V, P>,
        detail::mapped_type<V>,
        P,
        error::not_object>
{
private:
    V* value_;
    std::string key_;

public:
    converting_handler( V* v, P* p )
        : converting_handler::composite_handler(p), value_(v)
    {}

    bool signal_value(system::error_code&)
    {
        value_->emplace( std::move(key_), std::move(this->next_value_) );

        key_ = {};
        this->next_value_ = {};

        this->inner_active_ = false;

        return true;
    }

    bool on_object_begin( system::error_code& ec )
    {
        if( this->inner_active_ )
            return this->inner_.on_object_begin(ec);

        clear_container( *value_, inserter_implementation<V>() );
        return true;
    }

    bool on_object_end(system::error_code& ec)
    {
        if( this->inner_active_ )
            return this->inner_.on_object_end(ec);

        return this->parent_->signal_value(ec);
    }

    bool on_array_end( system::error_code& ec )
    {
        if( this->inner_active_ )
            return this->inner_.on_array_end(ec);

        return this->parent_->signal_end(ec);
    }

    bool on_key_part( system::error_code& ec, string_view sv )
    {
        if( this->inner_active_ )
            return this->inner_.on_key_part(ec, sv);

        key_.append( sv.data(), sv.size() );
        return true;
    }

    bool on_key( system::error_code& ec, string_view sv )
    {
        if( this->inner_active_ )
            return this->inner_.on_key(ec, sv);

        key_.append( sv.data(), sv.size() );

        this->inner_active_ = true;
        return true;
    }
};

// tuple handler
template<std::size_t I, class T>
struct handler_tuple_element
{
    template< class... Args >
    handler_tuple_element( Args&& ... args )
        : t_( static_cast<Args&&>(args)... )
    {}

    T t_;
};

template<std::size_t I, class T>
T&
get( handler_tuple_element<I, T>& e )
{
    return e.t_;
}

template<
    class P,
    class LV,
    class S = mp11::make_index_sequence<mp11::mp_size<LV>::value> >
struct handler_tuple;

template< class P, template<class...> class L, class... V, std::size_t... I >
struct handler_tuple< P, L<V...>, mp11::index_sequence<I...> >
    : handler_tuple_element<I, V>
    ...
{
    handler_tuple( handler_tuple const& ) = delete;
    handler_tuple& operator=( handler_tuple const& ) = delete;

    template< class Access, class T >
    handler_tuple( Access access, T* pv, P* pp )
        : handler_tuple_element<I, V>(
            access( pv, mp11::mp_size_t<I>() ),
            pp )
        ...
    {}
};

#if defined(BOOST_MSVC) && BOOST_MSVC < 1910

template< class T >
struct tuple_element_list_impl
{
    template< class I >
    using tuple_element_helper = tuple_element_t<I::value, T>;

    using type = mp11::mp_transform<
        tuple_element_helper,
        mp11::mp_iota< std::tuple_size<T> > >;
};
template< class T >
using tuple_element_list = typename tuple_element_list_impl<T>::type;

#else

template< class I, class T >
using tuple_element_helper = tuple_element_t<I::value, T>;
template< class T >
using tuple_element_list = mp11::mp_transform_q<
    mp11::mp_bind_back< tuple_element_helper, T>,
    mp11::mp_iota< std::tuple_size<T> > >;

#endif

template< class Op, class... Args>
struct handler_op_invoker
{
public:
    std::tuple<Args&...> args;

    template< class Handler >
    bool
    operator()( Handler& handler ) const
    {
        return (*this)( handler, mp11::index_sequence_for<Args...>() );
    }

private:
    template< class Handler, std::size_t... I >
    bool
    operator()( Handler& handler, mp11::index_sequence<I...> ) const
    {
        return Op()( handler, std::get<I>(args)... );
    }
};

template< class Handlers, class F >
struct tuple_handler_op_invoker
{
    Handlers& handlers;
    F fn;

    template< class I >
    bool
    operator()( I ) const
    {
        return fn( get<I::value>(handlers) );
    }
};

struct tuple_accessor
{
    template< class T, class I >
    auto operator()( T* t, I ) const -> tuple_element_t<I::value, T>*
    {
        using std::get;
        return &get<I::value>(*t);
    }
};

template< class T, class P >
class converting_handler<tuple_conversion_tag, T, P>
{

private:
    using ElementTypes = tuple_element_list<T>;

    template<class V>
    using ElementHandler = get_handler<V, converting_handler>;
    using InnerHandlers = mp11::mp_transform<ElementHandler, ElementTypes>;
    using HandlerTuple = handler_tuple<converting_handler, InnerHandlers>;

    T* value_;
    P* parent_;

    HandlerTuple handlers_;
    int inner_active_ = -1;

public:
    converting_handler( converting_handler const& ) = delete;
    converting_handler& operator=( converting_handler const& ) = delete;

    converting_handler( T* v, P* p )
        : value_(v) , parent_(p) , handlers_(tuple_accessor(), v, this)
    {}

    bool signal_value(system::error_code&)
    {
        ++inner_active_;
        return true;
    }

    bool signal_end(system::error_code& ec)
    {
        constexpr int N = std::tuple_size<T>::value;
        if( inner_active_ < N )
        {
            BOOST_JSON_FAIL( ec, error::size_mismatch );
            return false;
        }

        inner_active_ = -1;
        return parent_->signal_value(ec);
    }

#define BOOST_JSON_HANDLE_EVENT(fn) \
    struct do_ ## fn \
    { \
        template< class H, class... Args > \
        bool operator()( H& h, Args& ... args ) const \
        { \
            return h. fn (args...); \
        } \
    }; \
       \
    template< class... Args > \
    bool fn( system::error_code& ec, Args&& ... args ) \
    { \
        if( inner_active_ < 0 ) \
        { \
            BOOST_JSON_FAIL( ec, error::not_array ); \
            return false; \
        } \
        constexpr int N = std::tuple_size<T>::value; \
        if( inner_active_ >= N ) \
        { \
            BOOST_JSON_FAIL( ec, error::size_mismatch ); \
            return false; \
        } \
        using F = handler_op_invoker< do_ ## fn, system::error_code, Args...>; \
        using H = decltype(handlers_); \
        return mp11::mp_with_index<N>( \
            inner_active_, \
            tuple_handler_op_invoker<H, F>{ \
                handlers_, \
                F{ std::forward_as_tuple(ec, args...) } } ); \
    }

    BOOST_JSON_HANDLE_EVENT( on_object_begin )
    BOOST_JSON_HANDLE_EVENT( on_object_end )

    struct do_on_array_begin
    {
        HandlerTuple& handlers;
        system::error_code& ec;

        template< class I >
        bool operator()( I ) const
        {
            return get<I::value>(handlers).on_array_begin(ec);
        }
    };
    bool on_array_begin( system::error_code& ec )
    {
        if( inner_active_ < 0 )
        {
            inner_active_ = 0;
            return true;
        }

        constexpr int N = std::tuple_size<T>::value;

        if( inner_active_ >= N )
        {
            BOOST_JSON_FAIL( ec, error::size_mismatch );
            return false;
        }

        return mp11::mp_with_index<N>(
            inner_active_, do_on_array_begin{handlers_, ec} );
    }

    struct do_on_array_end
    {
        HandlerTuple& handlers;
        system::error_code& ec;

        template< class I >
        bool operator()( I ) const
        {
            return get<I::value>(handlers).on_array_end(ec);
        }
    };
    bool on_array_end( system::error_code& ec )
    {
        if( inner_active_ < 0 )
            return parent_->signal_end(ec);

        constexpr int N = std::tuple_size<T>::value;

        if( inner_active_ >= N )
            return signal_end(ec);

        return mp11::mp_with_index<N>(
            inner_active_, do_on_array_end{handlers_, ec} );
    }

    BOOST_JSON_HANDLE_EVENT( on_key_part )
    BOOST_JSON_HANDLE_EVENT( on_key )
    BOOST_JSON_HANDLE_EVENT( on_string_part )
    BOOST_JSON_HANDLE_EVENT( on_string )
    BOOST_JSON_HANDLE_EVENT( on_number_part )
    BOOST_JSON_HANDLE_EVENT( on_int64 )
    BOOST_JSON_HANDLE_EVENT( on_uint64 )
    BOOST_JSON_HANDLE_EVENT( on_double )
    BOOST_JSON_HANDLE_EVENT( on_bool )
    BOOST_JSON_HANDLE_EVENT( on_null )

#undef BOOST_JSON_HANDLE_EVENT
};

// described struct handler
#if defined(BOOST_MSVC) && BOOST_MSVC < 1910

template< class T >
struct struct_element_list_impl
{
    template< class D >
    using helper = described_member_t<T, D>;

    using type = mp11::mp_transform< helper, described_members<T> >;
};
template< class T >
using struct_element_list = typename struct_element_list_impl<T>::type;

#else

template< class T >
using struct_element_list = mp11::mp_transform_q<
    mp11::mp_bind_front< described_member_t, T >, described_members<T> >;

#endif

struct struct_accessor
{
    template< class T >
    auto operator()( T*, mp11::mp_size< described_members<T> > ) const
        -> void*
    {
        return nullptr;
    }

    template< class T, class I >
    auto operator()( T* t, I ) const
        -> described_member_t<T, mp11::mp_at< described_members<T>, I> >*
    {
        using Ds = described_members<T>;
        using D = mp11::mp_at<Ds, I>;
        return &(t->*D::pointer);
    }
};

struct struct_key_searcher
{
    string_view key;
    int& found;
    int index = 0;

    struct_key_searcher(string_view key, int& found) noexcept
        : key(key), found(found)
    {}

    template< class D >
    void
    operator()( D )
    {
        if( key == D::name )
            found = index;
        ++index;
    }
};

template<class P>
struct ignoring_handler
{
    P* parent_;
    std::size_t array_depth_ = 0;
    std::size_t object_depth_ = 0;

    ignoring_handler(ignoring_handler const&) = delete;
    ignoring_handler& operator=(ignoring_handler const&) = delete;

    ignoring_handler(void*, P* p) noexcept
        : parent_(p)
    {}

    bool on_object_begin(system::error_code&)
    {
        ++object_depth_;
        return true;
    }

    bool on_object_end(system::error_code& ec)
    {
        BOOST_ASSERT( object_depth_ > 0 );
        --object_depth_;

        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_array_begin(system::error_code&)
    {
        ++array_depth_;
        return true;
    }

    bool on_array_end(system::error_code& ec)
    {
        BOOST_ASSERT( array_depth_ > 0 );
        --array_depth_;

        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_end(ec);
        return true;
    }

    bool on_key_part(system::error_code&, string_view)
    {
        return true;
    }

    bool on_key(system::error_code&, string_view)
    {
        return true;
    }

    bool on_string_part(system::error_code&, string_view)
    {
        return true;
    }

    bool on_string(system::error_code& ec, string_view)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_number_part(system::error_code&)
    {
        return true;
    }

    bool on_int64(system::error_code& ec, std::int64_t)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_uint64(system::error_code& ec, std::uint64_t)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_double(system::error_code& ec, double)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_bool(system::error_code& ec, bool)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }

    bool on_null(system::error_code& ec)
    {
        if( (array_depth_ + object_depth_) == 0 )
            return parent_->signal_value(ec);
        return true;
    }
};

template<class V, class P>
class converting_handler<described_class_conversion_tag, V, P>
{
#if !defined(BOOST_DESCRIBE_CXX14)

    static_assert(
        sizeof(V) == 0, "Struct support for parse_into requires C++14" );

#else

private:
    using Dm = described_members<V>;
    using Dt = struct_element_list<V>;

    template<class T>
    using MemberHandler = get_handler<T, converting_handler>;
    using InnerHandlers = mp11::mp_push_back<
        mp11::mp_transform<MemberHandler, Dt>,
        ignoring_handler<converting_handler> >;
    using InnerCount = mp11::mp_size<InnerHandlers>;

    V* value_;
    P* parent_;

    std::string key_;

    handler_tuple<converting_handler, InnerHandlers> handlers_;
    int inner_active_ = -1;
    std::size_t activated_ = 0;

public:
    converting_handler( converting_handler const& ) = delete;
    converting_handler& operator=( converting_handler const& ) = delete;

    converting_handler( V* v, P* p )
        : value_(v), parent_(p), handlers_(struct_accessor(), v, this)
    {}

    struct is_required_checker
    {
        bool operator()( mp11::mp_size<Dt> ) const noexcept
        {
            return false;
        }

        template< class I >
        auto operator()( I ) const noexcept
        {
            using T = mp11::mp_at<Dt, I>;
            return !is_optional_like<T>::value;
        }
    };

    bool signal_value(system::error_code&)
    {
        BOOST_ASSERT( inner_active_ >= 0 );
        bool required_member = mp11::mp_with_index<InnerCount>(
            inner_active_,
            is_required_checker{});
        if( required_member )
            ++activated_;

        key_ = {};
        inner_active_ = -1;
        return true;
    }

    bool signal_end(system::error_code& ec)
    {
        key_ = {};
        inner_active_ = -1;
        return parent_->signal_value(ec);
    }

#define BOOST_JSON_INVOKE_INNER(fn) \
    if( inner_active_ < 0 ) \
    { \
        BOOST_JSON_FAIL( ec, error::not_object ); \
        return false; \
    } \
    auto f = [&](auto& handler) { return handler.fn ; }; \
    using F = decltype(f); \
    using H = decltype(handlers_); \
    return mp11::mp_with_index<InnerCount>( \
            inner_active_, \
            tuple_handler_op_invoker<H, F>{handlers_, f} );

    bool on_object_begin( system::error_code& ec )
    {
        if( inner_active_ < 0 )
            return true;

        BOOST_JSON_INVOKE_INNER( on_object_begin(ec) );
    }

    bool on_object_end( system::error_code& ec )
    {
        if( inner_active_ < 0 )
        {
            using C = mp11::mp_count_if<Dt, is_optional_like>;
            constexpr int N = mp11::mp_size<Dt>::value - C::value;
            if( activated_ < N )
            {
                BOOST_JSON_FAIL( ec, error::size_mismatch );
                return false;
            }

            return parent_->signal_value(ec);
        }

        BOOST_JSON_INVOKE_INNER( on_object_end(ec) );
    }

    bool on_array_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_begin(ec) );
    }

    bool on_array_end( system::error_code& ec )
    {
        if( inner_active_ < 0 )
            return parent_->signal_end(ec);

        BOOST_JSON_INVOKE_INNER( on_array_end(ec) );
    }

    bool on_key_part( system::error_code& ec, string_view sv )
    {
        if( inner_active_ < 0 )
        {
            key_.append( sv.data(), sv.size() );
            return true;
        }

        BOOST_JSON_INVOKE_INNER( on_key_part(ec, sv) );
    }

    bool on_key( system::error_code& ec, string_view sv )
    {
        if( inner_active_ >= 0 )
        {
            BOOST_JSON_INVOKE_INNER( on_key(ec, sv) );
        }

        string_view key = sv;
        if( !key_.empty() )
        {
            key_.append( sv.data(), sv.size() );
            key = key_;
        }

        inner_active_ = InnerCount::value - 1;
        mp11::mp_for_each<Dm>( struct_key_searcher(key, inner_active_) );
        return true;
    }

    bool on_string_part( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string_part(ec, sv) );
    }

    bool on_string( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string(ec, sv) );
    }

    bool on_number_part( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_number_part(ec) );
    }

    bool on_int64( system::error_code& ec, std::int64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_int64(ec, v) );
    }

    bool on_uint64( system::error_code& ec, std::uint64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_uint64(ec, v) );
    }

    bool on_double( system::error_code& ec, double v )
    {
        BOOST_JSON_INVOKE_INNER( on_double(ec, v) );
    }

    bool on_bool( system::error_code& ec, bool v )
    {
        BOOST_JSON_INVOKE_INNER( on_bool(ec, v) );
    }

    bool on_null( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_null(ec) );
    }

#undef BOOST_JSON_INVOKE_INNER

#endif
};

// variant handler
struct object_begin_handler_event
{ };

struct object_end_handler_event
{ };

struct array_begin_handler_event
{ };

struct array_end_handler_event
{ };

struct key_handler_event
{
    std::string value;
};

struct string_handler_event
{
    std::string value;
};

struct int64_handler_event
{
    std::int64_t value;
};

struct uint64_handler_event
{
    std::uint64_t value;
};

struct double_handler_event
{
    double value;
};

struct bool_handler_event
{
    bool value;
};

struct null_handler_event
{ };

using parse_event = variant2::variant<
    object_begin_handler_event,
    object_end_handler_event,
    array_begin_handler_event,
    array_end_handler_event,
    key_handler_event,
    string_handler_event,
    int64_handler_event,
    uint64_handler_event,
    double_handler_event,
    bool_handler_event,
    null_handler_event>;

template< class H >
struct event_visitor
{
    H& handler;
    system::error_code& ec;

    bool
    operator()(object_begin_handler_event&) const
    {
        return handler.on_object_begin(ec);
    }

    bool
    operator()(object_end_handler_event&) const
    {
        return handler.on_object_end(ec);
    }

    bool
    operator()(array_begin_handler_event&) const
    {
        return handler.on_array_begin(ec);
    }

    bool
    operator()(array_end_handler_event&) const
    {
        return handler.on_array_end(ec);
    }

    bool
    operator()(key_handler_event& ev) const
    {
        return handler.on_key(ec, ev.value);
    }

    bool
    operator()(string_handler_event& ev) const
    {
        return handler.on_string(ec, ev.value);
    }

    bool
    operator()(int64_handler_event& ev) const
    {
        return handler.on_int64(ec, ev.value);
    }

    bool
    operator()(uint64_handler_event& ev) const
    {
        return handler.on_uint64(ec, ev.value);
    }

    bool
    operator()(double_handler_event& ev) const
    {
        return handler.on_double(ec, ev.value);
    }

    bool
    operator()(bool_handler_event& ev) const
    {
        return handler.on_bool(ec, ev.value);
    }

    bool
    operator()(null_handler_event&) const
    {
        return handler.on_null(ec);
    }
};

// L<T...> -> variant< monostate, get_handler<T, P>... >
template< class P, class L >
using inner_handler_variant = mp11::mp_push_front<
    mp11::mp_transform_q<
        mp11::mp_bind_back<get_handler, P>,
        mp11::mp_apply<variant2::variant, L>>,
    variant2::monostate>;

template< class T, class P >
class converting_handler<variant_conversion_tag, T, P>
{
private:
    using variant_size = mp11::mp_size<T>;

    T* value_;
    P* parent_;

    std::string string_;
    std::vector< parse_event > events_;
    inner_handler_variant<converting_handler, T> inner_;
    int inner_active_ = -1;

public:
    converting_handler( converting_handler const& ) = delete;
    converting_handler& operator=( converting_handler const& ) = delete;

    converting_handler( T* v, P* p )
        : value_( v )
        , parent_( p )
    {}

    bool signal_value(system::error_code& ec)
    {
        inner_.template emplace<0>();
        inner_active_ = -1;
        events_.clear();
        return parent_->signal_value(ec);
    }

    bool signal_end(system::error_code& ec)
    {
        return parent_->signal_end(ec);
    }

    struct alternative_selector
    {
        converting_handler* self;

        template< class I >
        void
        operator()( I ) const
        {
            using V = mp11::mp_at<T, I>;
            auto& v = self->value_->template emplace<I::value>( V{} );
            self->inner_.template emplace<I::value + 1>(&v, self);
        }
    };
    void
    next_alternative()
    {
        if( ++inner_active_ >= static_cast<int>(variant_size::value) )
            return;

        mp11::mp_with_index< variant_size::value >(
            inner_active_, alternative_selector{this} );
    }

    struct event_processor
    {
        converting_handler* self;
        system::error_code& ec;
        parse_event& event;

        template< class I >
        bool operator()( I ) const
        {
            auto& handler = variant2::get<I::value + 1>(self->inner_);
            using Handler = remove_cvref<decltype(handler)>;
            return variant2::visit(
                event_visitor<Handler>{handler, ec}, event );
        }
    };
    bool process_events(system::error_code& ec)
    {
        constexpr std::size_t N = variant_size::value;

        // should be pointers not iterators, otherwise MSVC crashes
        auto const last = events_.data() + events_.size();
        auto first = last - 1;
        bool ok = false;

        if( inner_active_ < 0 )
            next_alternative();
        do
        {
            if( static_cast<std::size_t>(inner_active_) >= N )
            {
                BOOST_JSON_FAIL( ec, error::exhausted_variants );
                return false;
            }

            for ( ; first != last; ++first )
            {
                ok = mp11::mp_with_index< N >(
                    inner_active_, event_processor{this, ec, *first} );
                if( !ok )
                {
                    first = events_.data();
                    next_alternative();
                    ec.clear();
                    break;
                }
            }
        }
        while( !ok );

        return true;
    }

#define BOOST_JSON_INVOKE_INNER(ev, ec) \
    events_.emplace_back( ev ); \
    return process_events(ec);

    bool on_object_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( object_begin_handler_event{}, ec );
    }

    bool on_object_end( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( object_end_handler_event{}, ec );
    }

    bool on_array_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( array_begin_handler_event{}, ec );
    }

    bool on_array_end( system::error_code& ec )
    {
        if( !inner_active_ )
            return signal_end(ec);

        BOOST_JSON_INVOKE_INNER( array_end_handler_event{}, ec );
    }

    bool on_key_part( system::error_code&, string_view sv )
    {
        string_.append(sv);
        return true;
    }

    bool on_key( system::error_code& ec, string_view sv )
    {
        string_.append(sv);
        BOOST_JSON_INVOKE_INNER( key_handler_event{ std::move(string_) }, ec );
    }

    bool on_string_part( system::error_code&, string_view sv )
    {
        string_.append(sv);
        return true;
    }

    bool on_string( system::error_code& ec, string_view sv )
    {
        string_.append(sv);
        BOOST_JSON_INVOKE_INNER(
            string_handler_event{ std::move(string_) }, ec );
    }

    bool on_number_part( system::error_code& )
    {
        return true;
    }

    bool on_int64( system::error_code& ec, std::int64_t v )
    {
        BOOST_JSON_INVOKE_INNER( int64_handler_event{v}, ec );
    }

    bool on_uint64( system::error_code& ec, std::uint64_t v )
    {
        BOOST_JSON_INVOKE_INNER( uint64_handler_event{v}, ec );
    }

    bool on_double( system::error_code& ec, double v )
    {
        BOOST_JSON_INVOKE_INNER( double_handler_event{v}, ec );
    }

    bool on_bool( system::error_code& ec, bool v )
    {
        BOOST_JSON_INVOKE_INNER( bool_handler_event{v}, ec );
    }

    bool on_null( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( null_handler_event{}, ec );
    }

#undef BOOST_JSON_INVOKE_INNER
};

// optional handler
template<class V, class P>
class converting_handler<optional_conversion_tag, V, P>
{
private:
    using inner_type = value_result_type<V>;
    using inner_handler_type = get_handler<inner_type, converting_handler>;

    V* value_;
    P* parent_;

    inner_type inner_value_ = {};
    inner_handler_type inner_;
    bool inner_active_ = false;

public:
    converting_handler( converting_handler const& ) = delete;
    converting_handler& operator=( converting_handler const& ) = delete;

    converting_handler( V* v, P* p )
        : value_(v), parent_(p), inner_(&inner_value_, this)
    {}

    bool signal_value(system::error_code& ec)
    {
        *value_ = std::move(inner_value_);

        inner_active_ = false;
        return parent_->signal_value(ec);
    }

    bool signal_end(system::error_code& ec)
    {
        return parent_->signal_end(ec);
    }

#define BOOST_JSON_INVOKE_INNER(fn) \
    if( !inner_active_ ) \
        inner_active_ = true; \
    return inner_.fn;

    bool on_object_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_begin(ec) );
    }

    bool on_object_end( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_end(ec) );
    }

    bool on_array_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_begin(ec) );
    }

    bool on_array_end( system::error_code& ec )
    {
        if( !inner_active_ )
            return signal_end(ec);

        BOOST_JSON_INVOKE_INNER( on_array_end(ec) );
    }

    bool on_key_part( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_key_part(ec, sv) );
    }

    bool on_key( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_key(ec, sv) );
    }

    bool on_string_part( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string_part(ec, sv) );
    }

    bool on_string( system::error_code& ec, string_view sv )
    {
        BOOST_JSON_INVOKE_INNER( on_string(ec, sv) );
    }

    bool on_number_part( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_number_part(ec) );
    }

    bool on_int64( system::error_code& ec, std::int64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_int64(ec, v) );
    }

    bool on_uint64( system::error_code& ec, std::uint64_t v )
    {
        BOOST_JSON_INVOKE_INNER( on_uint64(ec, v) );
    }

    bool on_double( system::error_code& ec, double v )
    {
        BOOST_JSON_INVOKE_INNER( on_double(ec, v) );
    }

    bool on_bool( system::error_code& ec, bool v )
    {
        BOOST_JSON_INVOKE_INNER( on_bool(ec, v) );
    }

    bool on_null(system::error_code& ec)
    {
        if( !inner_active_ )
        {
            *value_ = {};
            return this->parent_->signal_value(ec);
        }
        else
        {
            return inner_.on_null(ec);
        }
    }

#undef BOOST_JSON_INVOKE_INNER
};

// path handler
template< class V, class P >
class converting_handler<path_conversion_tag, V, P>
    : public scalar_handler<P, error::not_string>
{
private:
    V* value_;
    bool cleared_ = false;

public:
    converting_handler( V* v, P* p )
        : converting_handler::scalar_handler(p)
        , value_(v)
    {}

    bool on_string_part( system::error_code&, string_view sv )
    {
        if( !cleared_ )
        {
            cleared_ = true;
            value_->clear();
        }

        value_->concat( sv.begin(), sv.end() );
        return true;
    }

    bool on_string(system::error_code& ec, string_view sv)
    {
        if( !cleared_ )
            value_->clear();
        else
            cleared_ = false;

        value_->concat( sv.begin(), sv.end() );

        return this->parent_->signal_value(ec);
    }
};

// into_handler
template< class V >
class into_handler
{
private:

    using inner_handler_type = get_handler<V, into_handler>;

    inner_handler_type inner_;
    bool inner_active_ = true;

public:

    into_handler( into_handler const& ) = delete;
    into_handler& operator=( into_handler const& ) = delete;

public:

    static constexpr std::size_t max_object_size = object::max_size();
    static constexpr std::size_t max_array_size = array::max_size();
    static constexpr std::size_t max_key_size = string::max_size();
    static constexpr std::size_t max_string_size = string::max_size();

public:

    explicit into_handler( V* v ): inner_( v, this )
    {
    }

    bool signal_value(system::error_code&)
    {
        return true;
    }

    bool signal_end(system::error_code&)
    {
        return true;
    }

    bool on_document_begin( system::error_code& )
    {
        return true;
    }

    bool on_document_end( system::error_code& )
    {
        inner_active_ = false;
        return true;
    }

#define BOOST_JSON_INVOKE_INNER(f) \
    if( !inner_active_ ) \
    { \
        BOOST_JSON_FAIL( ec, error::extra_data ); \
        return false; \
    } \
    else \
        return inner_.f

    bool on_object_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_begin(ec) );
    }

    bool on_object_end( std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_object_end(ec) );
    }

    bool on_array_begin( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_begin(ec) );
    }

    bool on_array_end( std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_array_end(ec) );
    }

    bool on_key_part( string_view sv, std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_key_part(ec, sv) );
    }

    bool on_key( string_view sv, std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_key(ec, sv) );
    }

    bool on_string_part( string_view sv, std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_string_part(ec, sv) );
    }

    bool on_string( string_view sv, std::size_t, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_string(ec, sv) );
    }

    bool on_number_part( string_view, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_number_part(ec) );
    }

    bool on_int64( std::int64_t v, string_view, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_int64(ec, v) );
    }

    bool on_uint64( std::uint64_t v, string_view, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_uint64(ec, v) );
    }

    bool on_double( double v, string_view, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_double(ec, v) );
    }

    bool on_bool( bool v, system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_bool(ec, v) );
    }

    bool on_null( system::error_code& ec )
    {
        BOOST_JSON_INVOKE_INNER( on_null(ec) );
    }

    bool on_comment_part(string_view, system::error_code&)
    {
        return true;
    }

    bool on_comment(string_view, system::error_code&)
    {
        return true;
    }

#undef BOOST_JSON_INVOKE_INNER
};

} // namespace detail
} // namespace boost
} // namespace json

#endif
