#pragma once

#include <atomic>
#include <memory>

#include "concurrentqueue/concurrentqueue.h"
#include "logger.h"

namespace flower {
template <typename T>
class object_pool {
    using object_ptr = std::shared_ptr<T>;

public:
    object_pool() = default;

    virtual ~object_pool() = default;

    template <typename... Args>
    void start(uint32_t init_count, uint32_t max_count, Args&&... args) {
        max_count_ = max_count;

        object_ptr object;
        for (uint32_t i = 0; i < init_count; ++i) {
            object = create(std::forward<Args>(args)...);
            if (!object) {
                LOG_ERROR("object_pool::start object is nullptr");
                return;
            }

            if (queue_.enqueue(object)) {
                ++cur_count_;
            }
        }
    }

    void stop() {
        max_count_ = 0;
        object_ptr object;
        while (queue_.try_dequeue(object)) {
            --cur_count_;
        }
    }

    template <typename... Args>
    object_ptr pop(Args&&... args) {
        object_ptr object;
        if (!queue_.try_dequeue(object)) {
            object = create(std::forward<Args>(args)...);
            if (!object) {
                LOG_ERROR("object_pool::pop object is nullptr");
                return object;
            }
            return object;
        }

        --cur_count_;
        return object;
    }

private:
    template <typename... Args>
    object_ptr create(Args&&... args) {
        return {new T(std::forward<Args>(args)...), [this](T* object) {
                    recovery(object);
                }};
    }

    void recovery(T* object) {
        if (cur_count_ < max_count_) {
            object->reset();
            if (queue_.enqueue({object, [this](T* object) {
                                    recovery(object);
                                }})) {
                ++cur_count_;
                return;
            }
        }

        object->~T();
    }

private:
    moodycamel::ConcurrentQueue<object_ptr> queue_{};
    std::atomic_uint32_t max_count_{};
    std::atomic_uint32_t cur_count_{};
};
}  // namespace flower