#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <list>
#include <thread>
#include <condition_variable>
#include <queue>
#include <functional>
#include <atomic>
#include <map>
#include <future>

// #include "spdlog/spdlog.h"
// #include "fmt/std.h"

/**
 * @brief   1、使用map存储工作线程,线程id作为键
 *          2、采用可变参数模板函数添加任务到任务队列中
 *          3、线程池采用单例模式
 *          4、有两种添加工作线程的方式,一种带有返回值,一种不带返回值
 * 
 * 使用方式:
 *     1、非成员函数 
 *          void func(int a, int b) { std::cout << a + b << std::endl; }
 *          CPPTP.add_task(func, 1, 2);
 *     2、成员函数 
 *          void A::func(int a, int b) { std::cout << a + b << std::endl; }
 *          A a;
 *          CPPTP.add_task(&A::func, &a, 1, 2);
 * 
 */

#define CPPTP ThreadPool::getInstance()

class ThreadPool
{
private:
    ThreadPool();
    ThreadPool(const ThreadPool &tp) = delete;
    ThreadPool &operator=(const ThreadPool &tp) = delete;


public:
    
    static ThreadPool& getInstance()
    {
        static ThreadPool tp;
        return tp;
    }

    ~ThreadPool();

    /**
     * @brief 向任务队列添加任务函数
     * 
     * @tparam F 函数(指针?)
     * @tparam Args 参数包
     * @param f 万能引用
     * @param args 万能引用
     */
    template <typename F, typename... Args>
    void add_task(F &&f, Args &&...args)
    {
        /* 将函数参数和函数绑定 */
        std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        /* 作用域是用于解锁lock */
        {
            std::unique_lock<std::mutex> lock(m_mutexTask);
            m_queue_Tasks.emplace(std::move(task));         /* 入队 */
        }
        /* 唤醒一个线程 */
        m_cond_Task.notify_one();
    }

    /* 添加带有返回值的任务函数
     * future内部定义了一个类型std::result_of<F(Args...)>::Type */
    template<typename F, typename... Args>
    auto add_task_with_ret(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>
    {
        /* 将类型去一个别名 */
        using  resultType = typename std::result_of<F(Args...)>::type;
        /* 将传入的函数与参数绑定并包装成一个可调用的指针,使用智能指针管理 */
        auto task = std::make_shared<std::packaged_task<resultType()>>(
            /* 使用forward完美转发,防止右值变成了左值 */
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        /* 获取future对象,用于获取返回值 */
        std::future<resultType> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(m_mutexTask);
            /* 使用lamba表达式调用封装的函数 */
            m_queue_Tasks.emplace([task](){
                (*task)();
            });
        }
        /* 解锁条件变量 */
        m_cond_Task.notify_one();
        /* 返回值,函数调用完成后,可以通过这个返回值获取任务的返回值 */
        return res;
    }

    /******  获取当前线程池在运行的线程个数,空闲线程的个数  ******/
    int getThreadMaxNum();                      /* 获取线程池最大线程的个数 */
    void setThreadMaxNum(int num);              /* 设置线程池最大线程的个数 */
    int getThreadMiniNum();                     /* 获取线程池最小线程的个数 */
    void setThreadMiniNum(int num);             /* 设置线程池最小线程的个数 */
    int getThreadIdleNum();                     /* 获取线程池空闲线程的个数 */
    int getThreadRunNum();                      /* 获取线程池正在运行的线程个数 */
    int getThreadLiveNum();                     /* 获取线程池现存的线程个数 */
    int getThreadAddNum();                      /* 线程池每次创建线程的个数 */
    void setThreadAddNum(int num);              /* 设置线程池每次创建线程的个数 */
    int getThreadMiniIdle();                    /* 线程池最小空闲线程的个数 */
    void setThreadMiniIdle(int num);            /* 设置线程池最小空闲线程的个数 */
    int getThreadMaxIdle();                     /* 线程池最大空闲线程的个数 */
    void setThreadMaxIdle(int num);             /* 设置线程池最大空闲线程的个数 */

private:
    void worker();                              /* 工作线程函数 */
    void managerThread();                       /* 维护线程池的线程,如提前创建线程,销毁线程 */
    void createThread(int num);                 /* 创建新的线程 */
    void clearThread();                         /* 清除失效的线程实例 */

private:
    class OneThread;                                    /* 类声明 */
    std::map<std::thread::id, std::thread> m_mapThreads;/* 线程数组,维护线程池 */
    std::list<std::thread::id> m_exitThreadID;          /* 已经退出任务函数的线程ID */
    std::queue<std::function<void(void)>> m_queue_Tasks;/* 任务队列,任务函数都是无返回值的函数 */
    std::condition_variable m_cond_Task;                /* 条件变量,没有任务的时候阻塞住 */

    bool m_stop;                                        /* 线程是否停止 */
    std::mutex m_mutexExitThreadID;                     /* 互斥锁,搭配环境变量使用,对已经退出的线程ID上锁 */
    std::mutex m_mutexTask;                             /* 互斥锁,搭配环境变量使用,对任务队列上锁 */
    std::thread m_managerThread;                        /* 管理线程 */

    int m_threadMaxNum = 256;                               /* 默认最大线程数 */
    int m_threadMiniNum = 5;                                /* 默认线程池中最小线程数 */
    std::atomic<int> m_threadAddNum;                        /* 每次需要添加的线程个数 */
    std::atomic<int> m_threadMiniIdle;                      /* 如果空闲线程小于这个数,就添加m_ThreadAddNum个线程 */
    std::atomic<int> m_threadMaxIdle;                       /* 最大空闲的线程个数,超过这个个数一定时间,就会销毁多余的 */

    std::atomic<int> m_threadRunNum;                        /* 正在运行的线程个数 */
    std::atomic<int> m_threadLiveNum;                       /* 现有的线程个数 */
    std::atomic<int> m_threadExitNum;                       /* 需要销毁的线程个数 */
};

#endif /* THREADPOOL_H */