namespace thread {

/**
    \brief Thread class for running member functions in a separate thread.

    Example:
    \code{.cpp}
    struct trooper {
        void figth(int num) {
            std::cerr << "fighting: " << num << '\n';
        }
    };

    trooper tr1;
    trooper tr2;
    thread para_trooper1(&trooper::fight, &tr1, 1);
    thread para_trooper2(&trooper::fight, &tr2, 2);
    \endcode

*/
struct thread
{
    /**
        \brief Creates a thread for a member function with no parameters.

        \param f Member function with signature T::*()
        \param t Object of type T where f can be called
    */
    template<typename F, typename T>
    thread(F f, T t) : joinable_(false), id_(0)
    {
        thread_helper_a0<F, T>* ph = new thread_helper_a0<F, T>(f, t);
        int const res = pthread_create(&id_, 0, &thread_helper_a0<F, T>::call, ph);
        if (res)
        {
            delete ph;
            throw std::runtime_error("thread creation failed");
        }
        joinable_ = true;
    }

    /**
        \brief Creates a thread for a member function with one parameters.

        \param f Member function with signature T::*(A)
        \param t Object of type T where f can be called
        \param a Parameter for thread function f
    */
    template<typename F, typename T, typename A>
    thread(F f, T t, A a) : joinable_(false), id_(0)
    {
        thread_helper_a1<F, T, A>* ph = new thread_helper_a1<F, T, A>(f, t, a);
        int const res = pthread_create(&id_, 0, &thread_helper_a1<F, T, A>::call, ph);
        if (res)
        {
            delete ph;
            throw std::runtime_error("thread creation failed");
        }
        joinable_ = true;
    }

    /**
        \brief Creates a thread for a member function with two parameters.

        \param f Member function with signature T::*(A)
        \param t Object of type T where f can be called
        \param a first parameter for thread function f
        \param b second parameter for thread function f
    */
    template<typename F, typename T, typename A, typename B>
    thread(F f, T t, A a, B b) : joinable_(false), id_(0)
    {
        thread_helper_a2<F, T, A, B>* ph = new thread_helper_a2<F, T, A, B>(f, t, a, b);
        int const res = pthread_create(&id_, 0, &thread_helper_a2<F, T, A, B>::call, ph);
        if (res)
        {
            delete ph;
            throw std::runtime_error("thread creation failed");
        }
        joinable_ = true;
    }

    /**
        \brief Join with thread if thread is joinable otherwise throws an
        exception of type std::runtime_error.
    */
    void join()
    {
        if (!joinable_)
            throw std::logic_error("thread not joinable");
        int const res = pthread_join(id_, 0);
        if (res)
            throw std::runtime_error("thread joining failed");
    }

    /**
        \brief Detaches thread if thread is joinable otherwise throws an
        exception of type std::runtime_exception.
    */
    void detach()
    {
        if (!joinable_)
            throw std::logic_error("thread not detachable");
        int const res = pthread_detach(id_);
        if (res)
            throw std::runtime_error("thread detaching failed");
    }

    ~thread()
    {
    }

private:
    template<typename F, typename T>
    struct thread_helper_a0
    {
        thread_helper_a0(F f, T t) : f(f), t(t) {}

        static void* call(void* data)
        {
            thread_helper_a0* th = (thread_helper_a0*)data;
            F f = th->f;
            T t = th->t;
            delete th;
            (t->*f)();
            return 0;
        }

        F f;
        T t;
    };

    template<typename F, typename T, typename A>
    struct thread_helper_a1
    {
        thread_helper_a1(F f, T t, A a) : f(f), t(t), a(a) {}

        static void* call(void* data)
        {
            thread_helper_a1* th = (thread_helper_a1*)data;
            F f = th->f;
            T t = th->t;
            A a = th->a;
            delete th;
            (t->*f)(a);
            return 0;
        }

        F f;
        T t;
        A a;
    };

    template<typename F, typename T, typename A, typename B>
    struct thread_helper_a2
    {
        thread_helper_a2(F f, T t, A a, B b) : f(f), t(t), a(a), b(b) {}

        static void* call(void* data)
        {
            thread_helper_a2* th = (thread_helper_a2*)data;
            F f = th->f;
            T t = th->t;
            A a = th->a;
            B b = th->b;
            delete th;
            (t->*f)(a,b);
            return 0;
        }

        F f;
        T t;
        A a;
        B b;
    };

    thread();
    thread(thread const&);
    thread const& operator=(thread const&);

    bool joinable_;
    pthread_t id_;
};



}