/**
 * @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_IWEAR_SCOPED_LOCK_H
#define __IWEAR_IWEAR_SCOPED_LOCK_H

#include <boost/utility/enable_if.hpp>
#include <iwear/exceptions.h>
#include <iwear/debug.h>

namespace iwear {
template<class T> struct lock_traits;

template<class T,
     int(T::* UnLock)(void) = &T::unlock,
     int(T::* Lock)(void) = &T::lock,
     int(T::* TryLock)(void) = Lock,
     int(T::* TimedLock)(float) = (int(T::*)(float))Lock
    >
class scoped_lock
{
    T* t;

    bool locked;

    template<class TT>
    typename boost::enable_if_c<lock_traits<TT>::has_lock,int>::type
    lock( )
    {
	if( ! locked )
	{
	    int ret = (t->*Lock)();
	    if( ! ret ) locked = true;
	    return ret;
	}
	else
	{
	    THROW(iwruntime_error,("Cannot lock already locked object"));
	}
    }

    template<class TT>
    typename boost::enable_if_c<lock_traits<TT>::has_try_lock,int>::type
    trylock( )
    {	
	if( ! locked )
	{
	    int ret = (t->*TryLock)();
	    if( ! ret ) locked = true;
	    return ret;
	}
	else
	{
	    THROW(iwruntime_error,("Cannot lock already locked object"));
	}
    }

    template<class TT>
    typename boost::enable_if_c<lock_traits<TT>::has_unlock,int>::type
    unlock( )
    {	
	if( locked )
	{
	    int ret = (t->*UnLock)();
	    if( ! ret ) locked = false;
	    return ret;
	}
	else
	{
	    THROW(iwruntime_error,("Cannot unlock already unlocked object"));
	}
    }

    template<class TT>
    typename boost::enable_if_c<lock_traits<TT>::has_timed_lock,int>::type
    lock( float to )
    {	
	if( ! locked )
	{
	    int ret = (t->*TimedLock)(to);
	    if( ! ret ) locked = true;
	    return ret;
	}
	else
	{
	    THROW(iwruntime_error,("Cannot lock already locked object"));
	}
    }
public:
    /**
     * @param il if this is true, then it means that the lock has already been
     * locked (e.g. from elsewhere) and that we do not need to lock it.
     */
    scoped_lock( T& _t, bool il = false ) : t(&_t), locked(il)
    {
	if( ! locked ) lock();
    }

    scoped_lock( T& _t, float to ) : t(&_t), locked(false)
    {
	lock(to);
    }


    ~scoped_lock( )
    {
	if( locked ) unlock();
    }

    int lock( )
    {
	return lock<T>();
    }

    int lock( float to )
    {
	return lock<T>( to );
    }

    int trylock( )
    {
	return trylock<T>();
    }

    int unlock( )
    {
	return unlock<T>();
    }

    operator void* (void)
    {
	return locked?this:0;
    }
};

template<class T>
struct lock_traits
{
    static const bool has_unlock = true;
    static const bool has_lock = true;
    static const bool has_try_lock = false;
    static const bool has_timed_lock = false;
};
/* test code
struct simple_lock_base
{
    virtual ~simple_lock_base() { }
    int lock( ) { 
	cout << __PRETTY_FUNCTION__ << endl;
	return 0; }
    int trylock( ) { 
	cout << __PRETTY_FUNCTION__ << endl;
	return 0; }
    int unlock( ) { 
	cout << __PRETTY_FUNCTION__ << endl;
	return 0; }
    int lock( float ) { 
	cout << __PRETTY_FUNCTION__ << endl;
	return 0; }
};

struct simple_lock : simple_lock_base test code
{
    typedef scoped_lock<simple_lock_base,
	    &simple_lock_base::unlock,
	    &simple_lock_base::lock,
	    &simple_lock_base::trylock,
	    &simple_lock_base::lock> 
		scoped_lock;
};
*/
}
#endif
