#pragma once

#ifndef THREADS_POOL_5DBCAAAD78EE4f66BB5B0FABCEAE0838
#define THREADS_POOL_5DBCAAAD78EE4f66BB5B0FABCEAE0838

#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <functional>

#include <util/generic/noncopyable.h>
#include <util/system/mutex.h>

#include <functional>
#include <queue>

#include <wmconsole/legacy/util/thread.h>
#include <wmconsole/legacy/util/thr_apps/close_event.h>
#include <wmconsole/legacy/util/safe_func.h>

namespace NWebmaster {
namespace threads {


template <unsigned char pool_size>
class thrs_pool : public TNonCopyable {
public:
    static const unsigned char g_pool_size = pool_size;
    typedef std::function<void()> t_func_type;

    thrs_pool();
    ~thrs_pool();

    inline void add_task(const t_func_type &task);
    inline void create_threads();
    inline void close_all();

private:
    void thr_func();
    inline void do_task();
    inline bool pop_task(t_func_type &func);

    TMutex locker;

    event finish_ev;
    event tasks_present_ev;
    std::function<void()> work_func;
    std::function<void()> safe_func;
    thread thrs[g_pool_size];
    std::queue<t_func_type> tasks;
};

template <typename t_thrs_pool>
class thrs_pool_recreator {
public:
    thrs_pool_recreator(t_thrs_pool &pool) : pool(pool) {
        pool.create_threads();
    }
    ~thrs_pool_recreator() {
        pool.close_all();
    }
private:
    t_thrs_pool &pool;
};

template <unsigned char pool_size>
thrs_pool<pool_size>::thrs_pool() {
    work_func = std::bind(&thrs_pool::thr_func, this);
    safe_func = std::bind(t_safe_func(), work_func);
    create_threads();
}

template <unsigned char pool_size>
thrs_pool<pool_size>::~thrs_pool() {
    close_all();
}

template <unsigned char pool_size>
void thrs_pool<pool_size>::create_threads() {
    finish_ev.reset();

    for (thread &thr : thrs) {
        thr = thread(safe_func);
    }
}

template <unsigned char pool_size>
void thrs_pool<pool_size>::close_all() {
    finish_ev.set();
    for (thread &thr : thrs) {
        thr.join();
    }
    finish_ev.reset();
}

template <unsigned char pool_size>
void thrs_pool<pool_size>::thr_func() {
    sigset_t   signal_mask;
    sigemptyset (&signal_mask);
    for (int i = SIGHUP; i <= SIGTERM; ++i) {
        sigaddset(&signal_mask, i);
    }
    int rc = pthread_sigmask(SIG_BLOCK, &signal_mask, nullptr);
    if (rc != 0) {
        /* handle error */
        log_info << "pthread_sigmask error";
    }

    event_holder<2> eh;
    eh.set_event<0>(finish_ev);
    eh.set_event<1>(tasks_present_ev);

    while (1 == eh.wait_any()) {
        do_task();
    }
}

template <unsigned char pool_size>
void thrs_pool<pool_size>::do_task() {
    t_func_type func;
    if (!pop_task(func)) {
        return;
    }
    only_log_safe_func(func);
}

template <unsigned char pool_size>
bool thrs_pool<pool_size>::pop_task(t_func_type &func) {
    TGuard<TMutex> guard(locker);

    if (tasks.empty()) {
        return false;
    }

    func = tasks.front();
    tasks.pop();

    if (tasks.empty()) {
        tasks_present_ev.reset();
    }

    return true;
}

template <unsigned char pool_size>
void thrs_pool<pool_size>::add_task(const t_func_type &task) {
    TGuard<TMutex> guard(locker);

    tasks.push(task);
    tasks_present_ev.set();
}

} //threads
} //namespace NWebmaster

#endif
