#include <iostream>

#include <entt/core/utility.hpp>
#include <entt/entity/poly_storage.hpp>
#include <entt/entity/registry.hpp>

template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);

template<typename Entity>
struct PolyStorage: entt::type_list_cat_t<
    decltype(as_type_list(std::declval<entt::Storage<Entity>>())),
    entt::type_list<
        void(const Entity *, const Entity *, void *),
        void(entt::basic_registry<Entity> &, const Entity, const void *),
        const void *(const Entity) const,
        void(entt::basic_registry<Entity> &) const
    >
> {
    using entity_type = Entity;
    using size_type = std::size_t;

    template<typename Base>
    struct type: entt::Storage<Entity>::template type<Base> {
        static constexpr auto base = decltype(as_type_list(std::declval<entt::Storage<Entity>>()))::size;

        void erase(entt::basic_registry<Entity> &owner, const entity_type *first, const entity_type *last) {
            entt::poly_call<base + 0>(*this, first, last, &owner);
        }

        void emplace(entt::basic_registry<Entity> &owner, const entity_type entity, const void *instance) {
            entt::poly_call<base + 1>(*this, owner, entity, instance);
        }

        const void * get(const entity_type entity) const {
            return entt::poly_call<base + 2>(*this, entity);
        }

        void copy_to(entt::basic_registry<Entity> &other) const {
            entt::poly_call<base + 3>(*this, other);
        }
    };

    template<typename Type>
    struct members {
        static void emplace(Type &self, entt::basic_registry<Entity> &owner, const entity_type entity, const void *instance) {
            self.emplace(owner, entity, *static_cast<const typename Type::value_type *>(instance));
        }

        static const typename Type::value_type * get(const Type &self, const entity_type entity) {
            return &self.get(entity);
        }

        static void copy_to(const Type &self, entt::basic_registry<entity_type> &other) {
            const entt::sparse_set &base = self;
            other.template insert<typename Type::value_type>(base.rbegin(), base.rend(), self.rbegin());
        }
    };

    template<typename Type>
    using impl = entt::value_list_cat_t<
        typename entt::Storage<Entity>::template impl<Type>,
        entt::value_list<
            &Type::template erase<const entity_type *>,
            &members<Type>::emplace,
            &members<Type>::get,
            &members<Type>::copy_to
        >
    >;
};

template<typename Entity>
struct entt::poly_storage_traits<Entity> {
    using storage_type = entt::poly<PolyStorage<Entity>>;
};


struct A {

};
struct B {
    int x;
};


int main (int argc, char* argv[])
{
    entt::registry registry;
    entt::registry other;
    entt::entity entities[10u];

    registry.create(std::begin(entities), std::end(entities));
    registry.insert<int>(std::begin(entities), std::end(entities), 42);
    registry.insert<char>(std::begin(entities), std::end(entities), 'c');
    registry.insert<B>(std::begin(entities), std::end(entities), {2});

    other.assign(registry.data(), registry.data() + registry.size(), registry.destroyed());
    registry.visit([&](const auto info) {
        std::as_const(registry).storage(info)->copy_to(other);
    });

    for(const auto entity: registry.view<B>()) {
        std::cout << entt::to_integral(entity);
        if (registry.any_of<B>(entity)) {
            std::cout << " registry: " << registry.get<B>(entity).x;
        }
        if (other.valid(entity)) {
            std::cout << " other: ";
            if (other.any_of<B>(entity)) {
                std::cout << other.get<B>(entity).x;
            }
        }
        std::cout << "\n";
    }
}