// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_THREAD_FUNCTIOn_
#define DLIB_THREAD_FUNCTIOn_
#include <memory>
#include "thread_function_extension_abstract.h"
#include "threads_kernel.h"
#include "auto_mutex_extension.h"
#include "threaded_object_extension.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
    class thread_function : private threaded_object
    {
        
        class base_funct
        {
        public:
            virtual void go() = 0;
            virtual ~base_funct() {}
        };
        template <typename F, typename T1, typename T2, typename T3, typename T4>
        class super_funct_4 : public base_funct
        {
        public:
            super_funct_4 ( F funct, T1 arg1, T2 arg2, T3 arg3, T4 arg4) :
                f(funct),
                a1(arg1),
                a2(arg2),
                a3(arg3),
                a4(arg4)
            {
            }
            void go() { f(a1, a2, a3, a4); }
            F f;
            T1 a1;
            T2 a2;
            T3 a3;
            T4 a4;
        };
        template <typename F, typename T1, typename T2, typename T3>
        class super_funct_3 : public base_funct
        {
        public:
            super_funct_3 ( F funct, T1 arg1, T2 arg2, T3 arg3):
                f(funct),
                a1(arg1),
                a2(arg2),
                a3(arg3)
            {
            }
            void go() { f(a1, a2, a3); }
            F f;
            T1 a1;
            T2 a2;
            T3 a3;
        };
        template <typename F, typename T1, typename T2>
        class super_funct_2 : public base_funct
        {
        public:
            super_funct_2 ( F funct, T1 arg1, T2 arg2) :
                f(funct),
                a1(arg1),
                a2(arg2)
            {
            }
            void go() { f(a1, a2); }
            F f;
            T1 a1;
            T2 a2;
        };
        template <typename F, typename T>
        class super_funct_1 : public base_funct
        {
        public:
            super_funct_1 ( F funct, T arg) : f(funct), a(arg)
            {
            }
            void go() { f(a); }
            F f;
            T a;
        };
        template <typename F>
        class super_funct_0 : public base_funct
        {
        public:
            super_funct_0 ( F funct) : f(funct)
            {
            }
            
            void go() { f(); }
            F f;
        };
    public:
        template <typename F>
        thread_function (
            F funct
        )
        {
            f.reset(new super_funct_0<F>(funct));
            start();
        }
        template <typename F, typename T>
        thread_function (
            F funct,
            T arg
        )
        {
            f.reset(new super_funct_1<F,T>(funct,arg));
            start();
        }
        template <typename F, typename T1, typename T2>
        thread_function (
            F funct,
            T1 arg1,
            T2 arg2
        )
        {
            f.reset(new super_funct_2<F,T1,T2>(funct, arg1, arg2));
            start();
        }
        template <typename F, typename T1, typename T2, typename T3>
        thread_function (
            F funct,
            T1 arg1,
            T2 arg2,
            T3 arg3
        )
        {
            f.reset(new super_funct_3<F,T1,T2,T3>(funct, arg1, arg2, arg3));
            start();
        }
        template <typename F, typename T1, typename T2, typename T3, typename T4>
        thread_function (
            F funct,
            T1 arg1,
            T2 arg2,
            T3 arg3,
            T4 arg4
        )
        {
            f.reset(new super_funct_4<F,T1,T2,T3,T4>(funct, arg1, arg2, arg3, arg4));
            start();
        }
        ~thread_function (
        )
        {
            threaded_object::wait();
        }
        bool is_alive (
        ) const
        {
            return threaded_object::is_alive();
        }
        void wait (
        ) const
        {
            threaded_object::wait();
        }
    private:
        void thread ()
        {
            f->go();
        }
        std::unique_ptr<base_funct> f;
        // restricted functions
        thread_function(thread_function&);        // copy constructor
        thread_function& operator=(thread_function&);    // assignment operator
    };
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREAD_FUNCTIOn_