// 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_