/**
 * @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_STOPWATCH_H
#define __IWEAR_STOPWATCH_H
#include <iwear/iwear.h>

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



namespace iwear
{

    namespace SW
    {
	enum WatchType 
	{
	    clock,
	    gettimeofday,
	    gettime
	};
    }

template< SW::WatchType WT = SW::gettimeofday >
class IWAPIT StopWatch 
{
private:
// approx resolution: 1000000 (1ms)
//    static const int clock_to_use = CLOCK_THREAD_CPUTIME_ID;
// approx resolution: 1000000 (1ms)
//    static const int clock_to_use = CLOCK_PROCESS_CPUTIME_ID;
//    The other two above seem to have only a low resolution
// approx resolution: 3000 (3us), seems to be the best for benchmarks here
    static const int clock_to_use = CLOCK_MONOTONIC;
// approx resolution: 3000 (3us)
//    static const int clock_to_use = CLOCK_REALTIME;

    union{
	timeval tv;
	clock_t ct;
	timespec ts;
    } start, end;
protected:
public:
    void Start( void );
    /**
     * Stops the stopwatch. Calling this multiple times will overwrite the the
     * interenal stored time with the one since last Start().
     */
    void Stop ( void );
    /**
     * @return time between last Start() and Stop() in seconds
     */
    double Time( void ) const;

    /**
     * @return seconds since epoch as simple double value (yes, its too big to
     * perfectly fit into a double, but the stopwatch seconds are small enough
     * to fit into it.
     */
    static double Now( void );
};

template<>
inline void StopWatch<SW::gettimeofday>::Start( void )
{
    gettimeofday(&start.tv,0);
}

template<>
inline void StopWatch<SW::gettime>::Start( void )
{
    clock_gettime(clock_to_use,&start.ts);
}

template<>
inline void StopWatch<SW::clock>::Start( void )
{
    start.ct = clock();
}

template<>
inline void StopWatch<SW::gettimeofday>::Stop ( void )
{
    /*
    end = start;
    end.tv_usec += 1;
    return;
    */
    gettimeofday(&end.tv,0);
}

template<>
inline void StopWatch<SW::gettime>::Stop ( void )
{
    clock_gettime(clock_to_use,&end.ts);
}

template<>
inline void StopWatch<SW::clock>::Stop ( void )
{
    end.ct = clock();
}

template<>
inline double StopWatch<SW::gettimeofday>::Time( void ) const
{
    double s = (end.tv.tv_sec - start.tv.tv_sec);
    s += (double)(end.tv.tv_usec - start.tv.tv_usec) / 1000000.0;
    return s;
}

template<>
inline double StopWatch<SW::clock>::Time( void ) const
{
    double s = double(end.ct-start.ct) / double(CLOCKS_PER_SEC);
    return s;
}

template<>
inline double StopWatch<SW::gettime>::Time( void ) const
{
    double s = (double)(end.ts.tv_sec - start.ts.tv_sec);
    s += (double)(end.ts.tv_nsec - start.ts.tv_nsec) / 1000000000.0;
    return s;
}

template<SW::WatchType WT>
double StopWatch<WT>::Now( void )
{
    double ret;
    timeval now;

    gettimeofday(&now,0);
    ret = (now.tv_sec);
    ret += (double)(now.tv_usec) / 1000000.0;
    return ret;
}

}

#endif
