// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_TIMEOUT_KERNEl_1_
#define DLIB_TIMEOUT_KERNEl_1_

#include "../threads.h"
#include "../algs.h"
#include "../misc_api.h"
#include "timeout_abstract.h"
#include "../uintn.h"
#include "../timer.h"

#ifdef _MSC_VER
// this is to disable the "'this' : used in base member initializer list"
// warning you get from some of the GUI objects since all the objects
// require that their parent class be passed into their constructor. 
// In this case though it is totally safe so it is ok to disable this warning.
#pragma warning(disable : 4355)
#endif // _MSC_VER

namespace dlib
{

// ----------------------------------------------------------------------------------------

    class timeout 
    {
        /*!
            INITIAL VALUE
                - b == a pointer to some kind of bind object

            CONVENTION
                - b == a pointer to some kind of bind object
        !*/

        class bind
        {
        public:
            virtual void go() = 0;
            virtual ~bind() {}
        };

        template <typename T>
        class functor : public bind
        {
        public:
            functor(const T& f) : function(f) {}
            T function;
            void go() { function(); }
        };

        template <typename T, typename R>
        class zero : public bind
        {
        public:
            T* object;
            R (T::*callback_function)();
            void go() { (object->*callback_function)(); }

        };

        template <typename T, typename R, typename U>
        class one : public bind
        {
        public:
            T* object;
            R (T::*callback_function)(U);
            U val;
            void go() { (object->*callback_function)(val); }
        };

    public:

        // This typedef is here for backwards compatibility with previous versions of dlib.
        typedef timeout kernel_1a;

        template <
            typename T
            >
        timeout (
            T callback_function,
            unsigned long ms_to_timeout
        ) :
            t(*this,&timeout::trigger_timeout)
        {
            b = new functor<T>(callback_function);
            t.set_delay_time(ms_to_timeout);
            t.start();
        }

        template <
            typename T
            >
        timeout (  
            T& object,
            void (T::*callback_function)(),
            unsigned long ms_to_timeout
        ): 
            t(*this,&timeout::trigger_timeout)
        {
            zero<T,void>* B = new zero<T,void>;
            b = B;
            B->object = &object;
            B->callback_function = callback_function;
            t.set_delay_time(ms_to_timeout);
            t.start();
        }

        template <
            typename T,
            typename U
            >
        timeout (  
            T& object,
            void (T::*callback_function)(U callback_function_argument),
            unsigned long ms_to_timeout,
            U callback_function_argument
        ): 
            t(*this,&timeout::trigger_timeout)
        {
            one<T,void,U>* B = new one<T,void,U>;
            b = B;
            B->object = &object; 
            B->callback_function = callback_function;
            B->val = callback_function_argument;
            t.set_delay_time(ms_to_timeout);
            t.start();
        }

        template <
            typename T
            >
        timeout (  
            T& object,
            int (T::*callback_function)(),
            unsigned long ms_to_timeout
        ): 
            t(*this,&timeout::trigger_timeout)
        {
            zero<T,int>* B = new zero<T,int>;
            b = B;
            B->object = &object;
            B->callback_function = callback_function;
            t.set_delay_time(ms_to_timeout);
            t.start();
        }

        template <
            typename T,
            typename U
            >
        timeout (  
            T& object,
            int (T::*callback_function)(U callback_function_argument),
            unsigned long ms_to_timeout,
            U callback_function_argument
        ): 
            t(*this,&timeout::trigger_timeout)
        {
            one<T,int,U>* B = new one<T,int,U>;
            b = B;
            B->object = &object; 
            B->callback_function = callback_function;
            B->val = callback_function_argument;
            t.set_delay_time(ms_to_timeout);
            t.start();
        }

        virtual ~timeout (
        )
        {
            t.stop_and_wait();
            delete b;
        }

    private:

        void trigger_timeout ()
        {
            b->go();
            t.stop();
        }

        dlib::timer<timeout> t;
        bind* b;

        // restricted functions
        timeout(const timeout&);        // copy constructor
        timeout& operator=(const timeout&);    // assignment operator

    };    

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_TIMEOUT_KERNEl_1_