#pragma once

#include <cstddef>
#include <list>
#include <deque>
#ifdef WITH_BOOST
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
#include <boost/container/pmr/synchronized_pool_resource.hpp>
#include <boost/container/vector.hpp>
using boost::container::pmr::synchronized_pool_resource;
using boost::container::pmr::polymorphic_allocator;
using boost::container::vector;
#else
#include <memory_resource>
using std::pmr::synchronized_pool_resource;
using std::pmr::polymorphic_allocator;
#endif
#include <unordered_set>
#ifdef WITH_BOOST
    template <class Key,
              class Hash = std::hash<Key>,
              class Pred = std::equal_to<Key>>
    using unordered_set = std::unordered_set<Key, Hash, Pred, polymorphic_allocator<Key>>;
#else
    using std::pmr::unordered_set;
    using std::vector;
#endif
namespace Xybrid::Util {
    extern synchronized_pool_resource rpool;
    extern polymorphic_allocator<std::max_align_t> ralloc;

    void reserveInitialPool();

    template<typename T, size_t PSIZE = 16>
    class ResourcePool {
        typedef char slot[sizeof(T)];
        std::list<slot[PSIZE]> pages;

        std::deque<T*> avail;
        unordered_set<T*> used = {rpool};

        void newPage() {
            auto p = pages.emplace_back();
            for (size_t i = 0; i < PSIZE; i++) avail.push_back(&p[i]);
            //avail.res
        }

    public:
        typedef T type;
        static const constexpr size_t pageSize = PSIZE;

        template<typename... Arg>
        T* getNew(Arg... args) {
            if (avail.empty()) newPage();
            T* n = avail.pop_back();
            new (n) T(args...);
            used.insert(n);
            return n;
        }

        void del(T* n) {
            if (!used.contains(n)) return; // not mine
            n->~T(); // destroy object
            used.erase(n);
            avail.push_back(n);
        }

    };
}
