/***************************************************************************
 *   Copyright (C) 2000-2010 by Noel Novelli                               *
 *   novelli@lif.univ-mrs.fr                                               *
 *                                                                         *
 *   This program 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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include <iostream>
#include <sstream>
#include <string>

using namespace std;

#include "threadTime.h"


ThreadTime::ThreadTime( )
{
}

int ThreadTime::ThreadTimeInit( )
{
#ifdef WIN32
    m_hThread = ::GetCurrentThread( );

	if (!::GetThreadTimes(m_hThread, &m_Creat, &m_Exit, &m_Kern, &m_User))
    {
		m_startTime = ::GetCurrentTime();
		return 0;
	}
	return 1;
#else
    return ::getrusage( RUSAGE_SELF, &m_usage ) == 0;
#endif
}

#ifdef WIN32
int ThreadTime::ThreadTimeElapsedSystemTime(SYSTEMTIME *elapsed) const
{
	FILETIME Creat, Exit, Kern, User;
	if (::GetThreadTimes(m_hThread, &Creat, &Exit, &Kern, &User))
    {
		LARGE_INTEGER IKern1, IUser1, IKern2, IUser2, IDiff;

		IKern1.LowPart  = m_Kern.dwLowDateTime;
        IKern1.HighPart = m_Kern.dwHighDateTime;
		
        IUser1.LowPart  = m_User.dwLowDateTime;
        IUser1.HighPart = m_User.dwHighDateTime;
		
        IKern2.LowPart  = Kern.dwLowDateTime;
        IKern2.HighPart = Kern.dwHighDateTime;
		
        IUser2.LowPart  = User.dwLowDateTime;
        IUser2.HighPart = User.dwHighDateTime;

		IDiff.QuadPart  = (IKern2.QuadPart + IUser2.QuadPart) -
                                            (IKern1.QuadPart + IUser1.QuadPart);

		Creat.dwLowDateTime  = IDiff.LowPart;
        Creat.dwHighDateTime = IDiff.HighPart;

		if (!::FileTimeToSystemTime(&Creat, elapsed))
            return 0;
	}
    else
        return 0;

	return 1;
}
#endif // WIN32


int ThreadTime::ThreadTimeElapsedTime( ostream &os /* = cerr*/ ) const
{
    string s;
    double d;
    
    if ( ! getElapsedTimeStrSec( s, d ) )
        return 0;
    
    os << s;
    
    return 1;
}

int ThreadTime::getElapsedTimeStrSec( string &sTime, double &timeInSec ) const
{
    stringstream streamTime;
    
#ifdef WIN32
	SYSTEMTIME tTime;

	if (ThreadTimeElapsedSystemTime(&tTime))
        {
            streamTime << tTime.wHour << " h " << tTime.wMinute << " m "
                    << tTime.wSecond << " s " << tTime.wMilliseconds << " ms";
            sTime = streamTime.str();
            timeInSec = tTime.wHour * 3600 + tTime.wMinute * 60 + tTime.wSecond + tTime.wMilliseconds / 1000.;
        }
        else
        {
            DWORD endTime = ::GetCurrentTime();
            streamTime << endTime - m_startTime;
            sTime = streamTime.str();
            timeInSec = endTime - m_startTime;
            return 0;
        }
        return 1;
#else
    struct rusage usage;
    long duree;
    long heure;
    long minute;
    long seconde;
    long milliseconde;

    ::getrusage( RUSAGE_SELF, &usage );
    milliseconde = (usage.ru_utime.tv_usec - m_usage.ru_utime.tv_usec) / 1000;
    duree = usage.ru_utime.tv_sec - m_usage.ru_utime.tv_sec;
    if ( milliseconde < 0 )
    {
        milliseconde += 1000;
        duree--;
    }
    
    heure = duree / 3600;
    minute = (duree - (heure * 3600)) / 60;
    seconde = duree % 60;

    streamTime << heure << " h " << minute << " m "
            << seconde << " s " << milliseconde << " ms";
    
    sTime = streamTime.str();
    timeInSec = duree + milliseconde / 1000.;
    
    return 1;
#endif // WIN32
}

int ThreadTime::getElapsedTimeSec( double &timeInSec ) const
{
#ifdef WIN32
	SYSTEMTIME tTime;

	if (ThreadTimeElapsedSystemTime(&tTime))
        {
            timeInSec = tTime.wHour * 3600 + tTime.wMinute * 60 + tTime.wSecond + tTime.wMilliseconds / 1000.;
        }
        else
        {
            DWORD endTime = ::GetCurrentTime();
            timeInSec = endTime - m_startTime;
            return 0;
        }
        return 1;
#else
    struct rusage usage;
    long duree;
    long milliseconde;

    ::getrusage( RUSAGE_SELF, &usage );
    milliseconde = (usage.ru_utime.tv_usec - m_usage.ru_utime.tv_usec) / 1000;
    duree = usage.ru_utime.tv_sec - m_usage.ru_utime.tv_sec;
    if ( milliseconde < 0 )
    {
        milliseconde += 1000;
        duree--;
    }
    
    timeInSec = duree + milliseconde / 1000.;
    
    return 1;
#endif // WIN32
}


void CThreadCumulatedTime::reset( )
{
    _dCumulTime = 0.;
    start( );
}

void CThreadCumulatedTime::start( )
{
    _threadTime.ThreadTimeInit( );
}

void CThreadCumulatedTime::stop( )
{
    double d;
    _threadTime.getElapsedTimeSec( d );
    _dCumulTime += d;
}

void CThreadCumulatedTime::getTimeStrSec( string &sTime, double &timeSec ) const
{
	long duree = (long) _dCumulTime;
	long milliseconde = (long) (_dCumulTime - (double) duree);
	
	long heure = duree / 3600;
	long minute = (duree - (heure * 3600)) / 60;
	long seconde = duree % 60;

	stringstream streamTime;
	streamTime << heure << " h " << minute << " m "
			<< seconde << " s " << milliseconde << " ms";
    
	sTime = streamTime.str();
	timeSec = _dCumulTime;
}

void CThreadCumulatedTime::getTimeSec( double &timeSec ) const
{
    timeSec = _dCumulTime;
}
