#pragma once

#include <functional>
#include <mutex>
#include <list>
#include <thread>
#include <memory>
#include <atomic>
#include <stdio.h>
#include <map>
#include <sstream>
#include <condition_variable>
#include "LockQueue.hpp"
#include <assert.h>

#define WThreadPool_log(fmt, ...) {printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}

#define WPOOL_MIN_THREAD_NUM 4
#define WPOOL_MAX_THREAD_NUM 256
#define WPOOL_MANAGE_SECONDS 20
#define ADD_THREAD_BOUNDARY 1

using EventFun = std::function<void ()>;
using int64 = long long int;

class WThreadPool
{
public:
    WThreadPool();
    virtual ~WThreadPool();

    static WThreadPool * globalInstance();

    void setMaxThreadNum(int maxNum);
    bool waitForDone(int waitMs = -1);

    template<typename Func, typename ...Arguments >
    void concurrentRun(Func func, Arguments... args) {
        EventFun queunFun = std::bind(func, args...);
        enQueueEvent(queunFun);
        if (((int)_workThreadList.size() < _maxThreadNum) &&
                (_eventQueue.size() >= ((int)_workThreadList.size() - _busyThreadNum - ADD_THREAD_BOUNDARY)))
        {
           _mgrCondVar.notify_one();
        }
        _workCondVar.notify_one();
    }

    template<typename T> static int64_t threadIdToint64(T threadId)
    {
        std::string stid;
        stid.resize(32);
        snprintf((char *)stid.c_str(), 32, "%lld", threadId);
        long long int tid = std::stoll(stid);
        return tid;
    }

private:
    int _minThreadNum = WPOOL_MIN_THREAD_NUM;
    int _maxThreadNum = 8;
    std::atomic<int> _busyThreadNum = {0};
    int _stepThreadNum = 4;
    volatile bool _exitAllFlag = false;
    std::atomic<int> _reduceThreadNum = {0};

    std::shared_ptr<std::thread> _mgrThread;
    LockQueue<EventFun> _eventQueue;
    std::list<std::shared_ptr<std::thread>> _workThreadList;

    std::mutex _threadIsRunMutex;
    std::map<std::thread::id, bool> _threadIsRunMap;

    std::condition_variable _workCondVar;
    std::mutex _workMutex;
    std::condition_variable _mgrCondVar;
    std::mutex _mgrMutex;

    static std::shared_ptr<WThreadPool> s_threadPool;
    static std::mutex s_globleMutex;

    void enQueueEvent(EventFun fun);
    EventFun  deQueueEvent();
    void run();
    void managerThread();
    void stop();
    void startWorkThread();
    void stopWorkThread();
    void adjustWorkThread();
};