/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_THREAD_H
#define __IWEAR_THREAD_H

#ifndef __IWEAR_THREADLOCKED_H
#include <iwear/threadlocked.h>
#endif

#include <set>
#include <list>
#include <boost/function.hpp>

extern "C" {
#include <stdint.h>
#include <pthread.h>
#include <setjmp.h>
}

#if defined(IW_SOLARIS) || defined(IW_MAC)
#define pthread_yield sched_yield
#endif

namespace iwear
{

class EventDispatcher;
/**
 * This is a lightweigth base class for threads. Its modeled roughly after the
 * concept of the CommonC++ thread class. But it lacks most of its advanced
 * features, but is lightweigth enough to be of use for most applications.
 * If you want to run something in a thread, you need your own class and derive
 * the Run function from this. If you already have a Functor available which
 * does your work, you might want to look at the ThreadExecutor class which
 * executes functors in their own thread.
 */
class IWAPIT Thread : public virtual ThreadLocked
{
private:

    static pthread_t main_thread;

    static Mutex* GlobalMutex;

    static uint32_t spawned_threads;

    static uint32_t active_threads;

    typedef std::list<boost::function<void(pthread_t,bool)> > function_list;

    typedef function_list::iterator thread_event;

    static function_list* registered_events;

    static void check_list( void );

    class IWLOCALT ThreadExitBall { };
    /**
     * C-Style wrapping function for usage of libc pthread_create call.
     */
IWLOCAL static void* start_thread_execution( void * );

    /**
     * Stores the actual pthread object.
     */
    pthread_t tid;

    /**
     * Stores the stack environment to be restored for an Exit() call.
     */
//    jmp_buf env;
    
    /**
     * Here some exception catching is done
     */
IWLOCAL void Trampoline( void );
protected:

    EventDispatcher& evdis;
    /**
     * A class needs to implement this. It will then be run in the new thread.
     * Exiting from this function means exiting this thread.
     *
     * @warning Run may never throw an exception. Doing this will lead to
     * undefined behaviour.
     */
IWAPI virtual void Run( void ) = 0;

    /**
     * This function is called if the Run() functions exits or the thread is
     * cancelled. Its run within the threads context, but in detached state.
     * @note This can be used to self-delete this object, since the Thread
     * implementation will do nothing else after calling this function.
     */
IWAPI virtual void Final( void ) { return; }

    /**
     * This function can be used to intentionally exit a thread (other than
     * exiting the Run() function).
     * It can only be used from within the thread itself.
     */
IWAPI void Exit( void );

    /**
     * Tests for Cancellation. Manually insertable cancellation point.
     */
IWAPI static inline void TestCancel( void ) { pthread_testcancel(); }

    /**
     * This yields the process, which means that it is put at the end of the
     * scheduling queue, but still in running state. If you want to yield this
     * thread in a sleeped state, you should better call usleep(1);
     */

IWAPI static inline void Yield( void ) { pthread_yield(); }
public:
    /**
     * Register some event notification. The passed functor will be called,
     * whenever a thread is created or destroyed. The passed parameters denote
     * the thread id, and the boolean flag tells if the thread was created.
     */
    static thread_event register_notification( const boost::function<void(pthread_t,bool)>& );

    /**
     * @warning You are only allowed to pass an actual thread_event you
     * previously got from a register_notification call, and you are not
     * allowed to pass it a second time, after it has been deregistered. It
     * will be invalid then.
     */
    static void deregister_notification( thread_event& te );



    static inline pthread_t self_id( void ) { return pthread_self(); }

IWAPI static pthread_t get_main_thread( void ) { return main_thread; }

    /**
     * Returns the number of currently running threads
     */
IWAPI static inline uint32_t get_active_threads( void ) { return active_threads; }

    /**
     * Returns the number of currently spawned threads (which might not
     * necessarily beeing executed)
     */
IWAPI static inline uint32_t get_spawned_threads( void ) { return spawned_threads; }
    /**
     * This can be used to externally terminate a Thread. It tries to cancel
     * and then to join on this thread. We currently only support deferred
     * cancellation which means that a thread is only cancelled at a
     * cancellation point. It might take quite some time until such a point is
     * reached, and until then this call will block.
     */
IWAPI void Terminate( void );

    /**
     * Create a thread.
     */
    Thread( EventDispatcher& evd ) IWAPI;

    EventDispatcher& get_eventdispatcher( void ) { return evdis; }

    /**
     * Destroyes this thread. It will internally call Terminate, so if you
     * delete or destroy a thread from another one, this might be affected by
     * the hanging of the call to Terminate.
     */
IWAPI ~Thread( void );

    /**
     * Because during the construction of the basic Thread object the virtual
     * function Run() is not yet available, you need to call Start() to start
     * the thread after you have created it.
     */
IWAPI void Start( void );

    /**
     * You can join on the exit of this thread.
     */
IWAPI int Join(void);

    /**
     * @returns true if this is the thread context of this thread object.
     */
IWAPI bool isThread( void ) { return (pthread_self() == tid); }
};

}

#endif

