/**
 * @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_CONDITIONAL_H
#define __IWEAR_CONDITIONAL_H

#ifndef __IWEAR_IWMUTEX_H
#include <iwear/iwmutex.h>
#endif

extern "C" {
#ifndef HAVE_CLOCK_GETTIME
#include <iwear/clock_gettime.h>
#endif
#include <unistd.h>
#include <errno.h>
}

#include <cmath>

namespace iwear
{

class Conditional/*{{{*/
{
private:
protected:
    pthread_cond_t cond;
    Mutex& mutex;

    bool waiting;
public:
    Conditional( Mutex& mutex_ ) : mutex(mutex_), waiting(false) 
    { 
	pthread_cond_init( &cond, NULL );
    }

    ~Conditional( )
    {
	pthread_cond_destroy( &cond );
    }

    /**
     * Using this function is a bit tricky. As the systems conditional
     * functions await the mutex beeing locked, we do this internally on
     * default. If the user wants to do it himself, he has to specify false
     * here, so we dont do it.
     * On a succesful return, the mutex is locked, so you have to unlock it
     * yourself. To make sure this is done, you should use a ThreadLocker.
     */
    int Wait( bool lock = true );
    int TimedWait( float s, bool lock = true );
    int Broadcast( void );

    /**
     * The parameter should be used for debug purposes only and specifies if
     * the signal will wait until really someone waits for the conditional. In
     * a usual program this should never be used.
     */
    int Signal( bool until = false);

    int Lock( void ) { return mutex.Lock(); }
    int Unlock( void ) { return mutex.Unlock(); }
    int TryLock( void ) { return mutex.TryLock(); }
};/*}}}*/

inline int Conditional::Wait( bool lock )/*{{{*/
{
    if( lock )
	mutex.Lock();
    waiting = true;
    int ret;
    while( (ret = pthread_cond_wait(&cond, &mutex.mutex)) == EINTR)
    {
    }
    return ret;
}/*}}}*/

inline int Conditional::TimedWait( float s, bool lock )/*{{{*/
{
    struct timespec timeout;

    clock_gettime( CLOCK_REALTIME, &timeout );
    timeout.tv_sec += static_cast<long int>(floor(s));
    timeout.tv_nsec += static_cast<long int>((s-floor(s)) * 1000000000.0);
    while( timeout.tv_nsec > 1000000000 )
    {
	timeout.tv_nsec -= 1000000000;
	timeout.tv_sec += 1;
    }

    if( lock )
	mutex.Lock();
    int ret;
    while( (ret = pthread_cond_timedwait( &cond, &mutex.mutex, &timeout )) == EINTR )
    {
    }
    return ret;
}/*}}}*/

inline int Conditional::Broadcast( void )/*{{{*/
{
    int ret;
    while( (ret = pthread_cond_broadcast(&cond)) == EINTR )
    {
    }
    return ret;
}/*}}}*/

inline int Conditional::Signal( bool until )/*{{{*/
{
    int ret;
    if( until )
    {
	while( ! waiting )
	{
//	    cout << "noone is waiting, waiting until someone is waiting" << endl;
	    pthread_yield();
	}
	waiting = false;
    }
    
    while( (ret =pthread_cond_signal(&cond)) == EINTR )
    {
    }
    return ret;
}/*}}}*/

}

#endif
