/**
 * ***README***
 * You will see that a LOT of code is being generated by these macros.
 * You might be worried about compile times... Of course, compile times will increase
 * but not by much. Stress tests show that compiling 1000 classes with 5 reflected members each,
 * of which three functions and two fields, takes about 5s on an i7 mobile CPU with latest GCC/Clang.
 *
 * There are efforts to reduce those further. Some optimizations are already in place,
 * for example the added template<Unused__> on member<N>s proved helpful. Remember that compilers
 * do NOT even attempt to generate code for templates which are not instantiated (in fact, they can't).
 * So no code is generated for member metadata that goes unused.
 *
 * The latest attempt to further reduce compile times was by removing the need to two invoke() functions, and
 * by using a unified auto_resolve function which resolves and returns the pointer to the function using provided types.
 * This required a lot of template utilities externally of what is generated through the macro, but reduced compile types by 17%.
 * This work resides in the experimental-pointer-dispatch branch and is currently being tested.
 *
 * Also note that the infrastructure for proxies, although it seems heavyweight, has only about 15% impact on compilation times.
 * If this is a problem for you, please open an issue and we might consider adding support for opting-out of proxies.
 */

struct Serializable : refl::attr::usage::member
{
};

class Shape {};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double getRadius() const;
    double getDiameter() const;
    double getArea() const;
};

REFL_AUTO(
    type(Circle, bases<>),
    func(getRadius),
    func(getDiameter),
    func(getArea, property("area"), Serializable())
)

/// --- ABOVE MACRO EXPANDS TO ---
namespace refl_impl::metadata {
    template<> struct type_info__<Circle> {
        typedef Circle type;
        static constexpr auto attributes{ ::refl::detail::make_attributes<::refl::attr::usage::type>(bases<>) };
        static constexpr auto name{ ::refl::util::make_const_string("Circle") };
        static constexpr size_t member_index_offset = 3 + 1;
        template <size_t N, typename = void>
        struct member {};

        template<typename Unused__> struct member<4 - member_index_offset, Unused__> {
            typedef ::refl::member::function member_type;
            static constexpr auto name{ ::refl::util::make_const_string("getRadius") };
            static constexpr auto attributes{ ::refl::detail::make_attributes<::refl::attr::usage::type>() };
        public:
            template<typename Self, typename... Args> static constexpr auto invoke(Self&& self, Args&&... args) -> decltype(std::declval<Self>().getRadius(::std::declval<Args>()...)) {
                return ::std::forward<Self>(self).getRadius(::std::forward<Args>(args)...);
            }
            template<typename... Args> static constexpr auto invoke(Args&&... args) -> decltype(::refl::detail::head_t<type, Args...>::getRadius(::std::declval<Args>()...)) {
                return ::refl::detail::head_t<type, Args...>::getRadius(::std::forward<Args>(args)...);
            }
            template <typename Dummy = void>
            static constexpr auto pointer() -> decltype(&::refl::detail::head_t<type, Dummy>::getRadius) { return &::refl::detail::head_t<type, Dummy>::getRadius; }

            template <typename Proxy> struct remap {
                template <typename... Args> decltype(auto) getRadius(Args&&... args) {
                    return Proxy::invoke_impl(static_cast<Proxy&>(*this), ::std::forward<Args>(args)...);
                }
                template <typename... Args> decltype(auto) getRadius(Args&&... args) const {
                    return Proxy::invoke_impl(static_cast<const Proxy&>(*this), ::std::forward<Args>(args)...);
                }
            };
        }; // struct member<4 - member_index_offset, Unused__>

        template<typename Unused__> struct member<5 - member_index_offset, Unused__> {
            typedef ::refl::member::function member_type;
            static constexpr auto name{ ::refl::util::make_const_string("getDiameter") };
            static constexpr auto attributes{ ::refl::detail::make_attributes<::refl::attr::usage::type>() };
        public:
            template<typename Self, typename... Args> static constexpr auto invoke(Self&& self, Args&&... args) -> decltype(std::declval<Self>().getDiameter(::std::declval<Args>()...)) {
                return ::std::forward<Self>(self).getDiameter(::std::forward<Args>(args)...);
            }
            template<typename... Args> static constexpr auto invoke(Args&&... args) -> decltype(::refl::detail::head_t<type, Args...>::getDiameter(::std::declval<Args>()...)) {
                return ::refl::detail::head_t<type, Args...>::getDiameter(::std::forward<Args>(args)...);
            }
            template <typename Dummy = void>
            static constexpr auto pointer() -> decltype(&::refl::detail::head_t<type, Dummy>::getDiameter) { return &::refl::detail::head_t<type, Dummy>::getDiameter; }

            template <typename Proxy> struct remap {
                template <typename... Args> decltype(auto) getDiameter(Args&&... args) {
                    return Proxy::invoke_impl(static_cast<Proxy&>(*this), ::std::forward<Args>(args)...);
                }
                template <typename... Args> decltype(auto) getDiameter(Args&&... args) const {
                    return Proxy::invoke_impl(static_cast<const Proxy&>(*this), ::std::forward<Args>(args)...);
                }
            };
        }; // struct member<5 - member_index_offset, Unused__>

        template<typename Unused__> struct member<6 - member_index_offset, Unused__> {
            typedef ::refl::member::function member_type;
            static constexpr auto name{ ::refl::util::make_const_string("getArea") };
            static constexpr auto attributes{ ::refl::detail::make_attributes<::refl::attr::usage::type>(property("area"), Serializable()) };
        public:
            template<typename Self, typename... Args> static constexpr auto invoke(Self&& self, Args&&... args) -> decltype(std::declval<Self>().getArea(::std::declval<Args>()...)) {
                return ::std::forward<Self>(self).getArea(::std::forward<Args>(args)...);
            }
            template<typename... Args> static constexpr auto invoke(Args&&... args) -> decltype(::refl::detail::head_t<type, Args...>::getArea(::std::declval<Args>()...)) {
                return ::refl::detail::head_t<type, Args...>::getArea(::std::forward<Args>(args)...);
            }
            template <typename Dummy = void>
            static constexpr auto pointer() -> decltype(&::refl::detail::head_t<type, Dummy>::getArea) { return &::refl::detail::head_t<type, Dummy>::getArea; }

            template <typename Proxy> struct remap {
                template <typename... Args> decltype(auto) getArea(Args&&... args) {
                    return Proxy::invoke_impl(static_cast<Proxy&>(*this), ::std::forward<Args>(args)...);
                }
                template <typename... Args> decltype(auto) getArea(Args&&... args) const {
                    return Proxy::invoke_impl(static_cast<const Proxy&>(*this), ::std::forward<Args>(args)...);
                }
            };
        }; // struct member<6 - member_index_offset, Unused__>

        static constexpr size_t member_count{ 7 - member_index_offset }; \
    }; // struct type_info__<Circle>
} // namespace refl_impl::metadata