#pragma once

#include <list>
#include "stdio.h"
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <utility>
//#include <logic_error>


typedef boost::unique_lock<boost::mutex> scoped_lock;

class Worker;
class ThreadPoolCachedEx;

template <typename T>
class Future{
public:
    Future(int id, Worker* workerPrm):
            done(false), canceled(false), taskId(id), worker(workerPrm){
//        workerWaitingMtx = new boost::mutex();
//        waitingCondition = new boost::condition_variable();
    }
    bool isDone() const {return done;}
    bool isCanceled() const {return canceled;}
    int getTaskId() const {return taskId;}
    void cancel();
    void setWorker(Worker* worker); //@FIXME friends? other encapsulating?
    void setResult(void* result); //@FIXME friends? other encapsulating?
    T get();
private:
    bool done;
    bool canceled;
    const int taskId;
    Worker* worker;
    T ret;
    boost::mutex workerWaitingMtx;
    boost::condition_variable waitingCondition;
    void waitingForWorker();
};

template <typename T>
class Callable{
public:
    Callable():taskId(generateTaskId()){}
    virtual T call() = 0;
    int getTaskId() const{return taskId;}
private:
    int generateTaskId(){
        static volatile int N = 0;
        return N++;
    }

    const int taskId;
};

template <typename T>
class Runnable{
public:
    Runnable():taskId(generateTaskId()){}
    virtual void run() = 0;
    int getTaskId() const{return taskId;}
private:
    int generateTaskId(){
        static volatile int N = 0;
        return N++;
    }

    const int taskId;
};

template<typename T>
class ExecutionUnit{
public:
    Future<T>* future;
    Callable<T>* task;
    Runnable<T>* runable;
    Worker* worker;    
    ExecutionUnit(Future<T>* futureParam, Callable<T>* taskParam, Worker* workerParam):
        future(futureParam), task(taskParam), worker(workerParam), runable(0){}
    ExecutionUnit(Future<T>* futureParam, Runnable<T>* taskParam, Worker* workerParam):
        future(futureParam), runable(taskParam), worker(workerParam), task(0){}
};

class ThreadPoolCachedEx{
public:
    inline ThreadPoolCachedEx(const int hotThreads, const int maxThreads, const double timeout);
    inline virtual ~ThreadPoolCachedEx();
    template <typename T>
    Future<T>* submit(Callable<T>* c);
    template <typename T>
    Future<T>* submit(Runnable<T>* task);
    int getHotThreads() const {return hotThreads;}
    int getTimeout() const {return timeout;}
    int getActualWorkersCount() const {return workers.size();}
    inline ExecutionUnit<void*>* findTask();
    boost::condition_variable queue_notifier;
    boost::condition_variable pool_deletition_notifier;
    boost::mutex queueMtx;
    boost::mutex deletingMtx;
    inline void tryToRemoveWorker(Worker* worker);
    bool isShutdown() {return false;}   //TODO: isShutdown
    void shutdown() {} //TODO: shutdown

private:
    std::list<Worker* > workers;
    inline void startNewWorker();
    const int hotThreads;
    const int maxThreads;
    const int timeout;
    std::list<void* > tasksQueue;
};

class Worker{
public:
    Worker(ThreadPoolCachedEx* poolPrm) : pool(poolPrm), workerId(generateWorkerId())
    {
        mtx = new boost::mutex();
        deleted = false;
        executionUnit = 0;
        waiting = true;
        //printf("construct worker %d\n", workerId);
    }

    template <typename T>
    void setTask(const ExecutionUnit<T>*);
    bool isWaiting() const {return waiting;}
    void setWaiting(bool waiting){this->waiting = waiting;}
    void run() {
        while(true){
            waitForTask();
            if (deleted){
                cleans();
                return;
            }
            scoped_lock lock(*mtx);
            if (this->executionUnit->future->isCanceled()){
                //printf("wow, task canceled\n");
                setWaiting(true);
                executionUnit = 0;
                continue;
            }
            this->executionUnit->future->setWorker(this);
            //printf("worker %d start calc\n", workerId);
            void* ret = 0;
            if (this->executionUnit->task)
                ret = this->executionUnit->task->call();
            else if (this->executionUnit->runable)
                this->executionUnit->runable->run();
            //printf("end calc\n");
            this->executionUnit->future->setResult(ret);
            //printf("res setted\n");
        }
    }
    void cleans() {
        //printf("time to die worker %d. pool = %p\n", workerId, pool);
        thread.interrupt();
        scoped_lock queueLock(pool->deletingMtx);
        pool->tryToRemoveWorker(this);
        pool->pool_deletition_notifier.notify_one();
        return;
        //@TODO killing workers
        //
        //delete mtx;
        boost::mutex* localMtx = mtx;
        {
            // scoped_lock lock(*localMtx);
            delete this;
        }
        delete localMtx;

    }
    boost::thread thread;
    bool deleted;
    ThreadPoolCachedEx* pool;
    boost::mutex* mtx;
    ExecutionUnit<void*>* executionUnit;
private:
    bool waiting;
    void waitForTask() {
        scoped_lock queueLock(this->pool->queueMtx);
        if (deleted){
            return;
        }
        //printf("worker %d go to find task\n");
        this->executionUnit =  pool->findTask();
        while (this->executionUnit == 0){
            if (deleted){
                return;
            }
            boost::system_time tAbsoluteTime =
                boost::get_system_time() + boost::posix_time::milliseconds(pool->getTimeout() * 1000);
            //printf("worker %d start sleeping\n", workerId);
            this->pool->queue_notifier.timed_wait(queueLock, tAbsoluteTime);
            if (boost::get_system_time() >= tAbsoluteTime){
                pool->tryToRemoveWorker(this);
            }
            this->executionUnit = pool->findTask();
            //printf("worker %d stop sleeping\n", workerId);

        }
        setWaiting(false);
    }
    int generateWorkerId(){
        static volatile int N = 0;
        return (int)this;
    }

    const int workerId;
};

#include "util/ThreadPoolCachedEx.cpp"



