// =====================================================================
//  $Id: TSystemTimer.cc,v 1.6 2004/03/07 10:30:34 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TSystemTimer.cc,v $
//  Revision 1.6  2004/03/07 10:30:34  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.5  2004/03/07 07:29:38  goiwai
//  TSystemTimer::WhatTimeIsItNow()が実装されてなかったので実装しました。
//
//  Revision 1.4  2003/10/06 16:38:26  goiwai
//  *** empty log message ***
//
//  Revision 1.3  2003/07/30 16:21:21  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TSystemTimer.hh"
#include "TSystemClock.hh"

static const Tdouble _clktck = (Tdouble)( sysconf( _SC_CLK_TCK ) );

TSystemTimer::TSystemTimer( const Tstring& unit )
  : theStatus( tUndefined ),
    theUnit( unit ),
    theBeginOfRealTime(),
    theEndOfRealTime(),
    theBeginOfSystemTime(),
    theEndOfSystemTime(),
    theBeginOfUserTime(),
    theEndOfUserTime(),
    thePausedTime(),
    theRunningTime(),
    theIdlingTime(),
    theRealLapTime(),
    theSystemLapTime(),
    theUserLapTime()
{
  initialize();
}

TSystemTimer::TSystemTimer( const TSystemTimer& right )
  : theStatus( right.theStatus ),
    theUnit( right.theUnit ),
    theBeginOfRealTime( right.theBeginOfRealTime ),
    theEndOfRealTime( right.theEndOfRealTime ),
    theBeginOfSystemTime( right.theBeginOfSystemTime ),
    theEndOfSystemTime( right.theEndOfSystemTime ),
    theBeginOfUserTime( right.theBeginOfUserTime ),
    theEndOfUserTime( right.theEndOfUserTime ),
    thePausedTime( right.thePausedTime ),
    theRunningTime( right.theRunningTime ),
    theIdlingTime( right.theIdlingTime ),
    theRealLapTime( right.theRealLapTime ),
    theSystemLapTime( right.theSystemLapTime ),
    theUserLapTime( right.theUserLapTime )
{;}

TSystemTimer::~TSystemTimer()
{;}

const TSystemTimer& TSystemTimer::operator=( const TSystemTimer& right )
{
  theStatus = right.theStatus;
  theUnit = right.theUnit;
  theBeginOfRealTime = right.theBeginOfRealTime;
  theEndOfRealTime = right.theEndOfRealTime;
  theBeginOfSystemTime = right.theBeginOfSystemTime;
  theEndOfSystemTime = right.theEndOfSystemTime;
  theBeginOfUserTime = right.theBeginOfUserTime;
  theEndOfUserTime = right.theEndOfUserTime;
  thePausedTime = right.thePausedTime;
  theRunningTime = right.theRunningTime;
  theIdlingTime = right.theIdlingTime;
  theRealLapTime = right.theRealLapTime;
  theSystemLapTime = right.theSystemLapTime;
  theUserLapTime = right.theUserLapTime;
  return *this;
}

Tvoid TSystemTimer::Start()
{
  if ( theStatus == tReady ) {
    start();
    return;
  } else if ( theStatus == tIdle ) {
    restart();
    return;
  } else {
    return;
  }
}

Tvoid TSystemTimer::Pause()
{
  if ( theStatus == tRunning ) {
    pause();
    return;
  } else {
    return;
  }
}

Tvoid TSystemTimer::Lap()
{
  if ( theStatus == tRunning ) {
    lap();
    return;
  } else if ( theStatus == tIdle ) {
    restart();
    lap();
    pause();
    return;
  } else {
    return;
  }
}

Tvoid TSystemTimer::Restart()
{
  if ( theStatus == tIdle ) {
    restart();
    return;
  } else {
    return;
  }
}

Tvoid TSystemTimer::Stop()
{
  if ( theStatus == tIdle ) {
    restart();
    stop();
    return;
  } else if ( theStatus == tRunning ) {
    stop();
    return;
  } else {
    return;
  }
}

Tvoid TSystemTimer::initialize()
{
  clear();
  theStatus = tReady;
  return;
}

Tvoid TSystemTimer::clear()
{
  theBeginOfRealTime = DBL_MIN;
  theEndOfRealTime = DBL_MIN;
  theBeginOfSystemTime = DBL_MIN;
  theEndOfSystemTime = DBL_MIN;
  theBeginOfUserTime = DBL_MIN;
  theEndOfUserTime = DBL_MIN;
  thePausedTime = 0.0;
  theRunningTime = 0.0;
  theIdlingTime = 0.0;
  theRealLapTime.clear();
  theSystemLapTime.clear();
  theUserLapTime.clear();
  return;
}

Tvoid TSystemTimer::start()
{
  clear();
  struct tms buf;
  Tclock_t clk = times( &buf ); 
  if ( clk == -1 ) {
    perror( "TSystemTimer::start" );
    return;
  }

  theBeginOfRealTime = ( (Tdouble)( clk ) / _clktck );
  theBeginOfSystemTime = ( (Tdouble)( buf.tms_stime ) / _clktck );
  theBeginOfUserTime = ( (Tdouble)( buf.tms_utime ) / _clktck );

  theStatus = tRunning;

  return;
}

Tvoid TSystemTimer::stop()
{
  struct tms buf;
  Tclock_t clk = times( &buf ); 
  if ( clk == -1 ) {
    perror( "TSystemTimer::start" );
    return;
  }

  theEndOfRealTime = ( (Tdouble)( clk ) / _clktck );
  theEndOfSystemTime = ( (Tdouble)( buf.tms_stime ) / _clktck );
  theEndOfUserTime = ( (Tdouble)( buf.tms_utime ) / _clktck );
  theRunningTime = theEndOfRealTime - theBeginOfRealTime - theIdlingTime;

  theStatus = tReady;

  return;
}

Tvoid TSystemTimer::restart()
{
  struct tms buf;
  Tclock_t clk = times( &buf ); 
  if ( clk == -1 ) {
    perror( "TSystemTimer::restart" );
    return;
  }

  Tdouble restarted = ( (Tdouble)( clk ) / _clktck );
  theIdlingTime += ( restarted - thePausedTime );

  theStatus = tRunning;
  return;
}

Tvoid TSystemTimer::pause()
{
  struct tms buf;
  Tclock_t clk = times( &buf ); 
  if ( clk == -1 ) {
    perror( "TSystemTimer::pause" );
    return;
  }

  thePausedTime = ( (Tdouble)( clk ) / _clktck );

  theStatus = tIdle;
  return;
}

Tvoid TSystemTimer::lap()
{
  struct tms buf;
  Tclock_t clk = times( &buf ); 
  if ( clk == -1 ) {
    perror( "TSystemTimer::lap" );
    return;
  }

  Tdouble realbuf = ( (Tdouble)( clk ) / _clktck );
  Tdouble sysbuf = ( (Tdouble)( buf.tms_stime ) / _clktck );
  Tdouble userbuf = ( (Tdouble)( buf.tms_utime ) / _clktck );

  Tdouble factor = 1.0;
  if ( theUnit == Tmsec ) {
    factor = 1.0e+03;
  } else if ( theUnit == Tusec ) {
    factor = 1.0e+06;
  } else if ( theUnit == Tnsec ) {
    factor = 1.0e+09;
  } else {
    SetUnit( Tsec );
  }

  theRealLapTime.push_back( ( realbuf - theBeginOfRealTime ) * factor );
  theSystemLapTime.push_back( ( sysbuf - theBeginOfSystemTime ) * factor );
  theUserLapTime.push_back( ( userbuf - theBeginOfUserTime ) * factor );

  return;
}

Tdouble TSystemTimer::GetRealElapsedTime()
{
  if ( theStatus == tRunning || theStatus == tIdle || theStatus == tReady ) {
    struct tms buf;
    Tclock_t clk = times( &buf ); 
    if ( clk == -1 ) {
      perror( "TSystemTimer::GetRealElapsedTime" );
      return DBL_MIN;
    }
    Tdouble realbuf = ( (Tdouble)( clk ) / _clktck );
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( realbuf - theBeginOfRealTime ) * factor;
  } else {
    return 0.0;
  }
}

Tdouble TSystemTimer::GetSystemElapsedTime()
{
  if ( theStatus == tRunning || theStatus == tIdle || theStatus == tReady ) {
    struct tms buf;
    Tclock_t clk = times( &buf ); 
    if ( clk == -1 ) {
      perror( "TSystemTimer::GetSystemElapsedTime" );
      return DBL_MIN;
    }
    Tdouble sysbuf = ( (Tdouble)( buf.tms_stime ) / _clktck );
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( sysbuf - theBeginOfSystemTime ) * factor;
  } else {
    return 0.0;
  }
}

Tdouble TSystemTimer::GetUserElapsedTime()
{
  if ( theStatus == tRunning || theStatus == tIdle || theStatus == tReady ) {
    struct tms buf;
    Tclock_t clk = times( &buf );
    if ( clk == -1 ) {
      perror( "TSystemTimer::GetUserElapsedTime" );
      return DBL_MIN;
    }
    Tdouble userbuf = ( (Tdouble)( buf.tms_utime ) / _clktck );
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( userbuf - theBeginOfUserTime ) * factor;
  } else {
    return 0.0;
  }
}

Tdouble TSystemTimer::GetTotalRunningTime()
{
  if ( theStatus == tRunning || theStatus == tReady ) {
    struct tms buf;
    Tclock_t clk = times( &buf );
    if ( clk == -1 ) {
      perror( "TSystemTimer::GetTotalRunningTime" );
      return DBL_MIN;
    }
    Tdouble realbuf = ( (Tdouble)( clk ) / _clktck );
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( realbuf - theBeginOfRealTime - theIdlingTime ) * factor;
  } else if ( theStatus == tIdle ) {
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( thePausedTime - theBeginOfRealTime - theIdlingTime ) * factor;
  } else {
    return 0.0;
  }
}

Tdouble TSystemTimer::GetTotalIdlingTime()
{
  if ( theStatus == tRunning || theStatus == tReady ) {
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return theIdlingTime * factor;
  } else if ( theStatus == tIdle ) {
    struct tms buf;
    Tclock_t clk = times( &buf );
    if ( clk == -1 ) {
      perror( "TSystemTimer::GetTotalIdlingTime" );
      return DBL_MIN;
    }
    Tdouble realbuf = ( (Tdouble)( clk ) / _clktck );
    Tdouble factor = 1.0;
    if ( theUnit == Tmsec ) {
      factor = 1.0e+03;
    } else if ( theUnit == Tusec ) {
      factor = 1.0e+06;
    } else if ( theUnit == Tnsec ) {
      factor = 1.0e+09;
    } else {
      SetUnit( Tsec );
    }
    return ( realbuf - thePausedTime + theIdlingTime ) * factor;
  } else {
    return 0.0;
  }
}

Tvoid TSystemTimer::SetUnit( const Tstring& unit )
{
  Tdouble f = 1.0;
  if ( theUnit == unit ) {
    return;


  } else if ( theUnit == Tsec && unit == Tsec ) {
    f = 1.0;
  } else if ( theUnit == Tsec && unit == Tmsec ) {
    f = 1.0e+03;
  } else if ( theUnit == Tsec && unit == Tusec ) {
    f = 1.0e+06;
  } else if ( theUnit == Tsec && unit == Tnsec ) {
    f = 1.0e+09;


  } else if ( theUnit == Tmsec && unit == Tsec ) {
    f = 1.0e-03;
  } else if ( theUnit == Tmsec && unit == Tmsec ) {
    f = 1.0;
  } else if ( theUnit == Tmsec && unit == Tusec ) {
    f = 1.0e+03;
  } else if ( theUnit == Tmsec && unit == Tnsec ) {
    f = 1.0e+06;


  } else if ( theUnit == Tusec && unit == Tsec ) {
    f = 1.0e-06;
  } else if ( theUnit == Tusec && unit == Tmsec ) {
    f = 1.0e-03;
  } else if ( theUnit == Tusec && unit == Tusec ) {
    f = 1.0;
  } else if ( theUnit == Tusec && unit == Tnsec ) {
    f = 1.0e+03;


  } else if ( theUnit == Tnsec && unit == Tsec ) {
    f = 1.0e-09;
  } else if ( theUnit == Tnsec && unit == Tmsec ) {
    f = 1.0e-06;
  } else if ( theUnit == Tnsec && unit == Tusec ) {
    f = 1.0e-03;
  } else if ( theUnit == Tnsec && unit == Tnsec ) {
    f = 1.0;


  } else {
    return;
  }
  convertTimeScale( f );
  theUnit = unit;
  return;
}

Tvoid TSystemTimer::convertTimeScale( Tdouble factor )
{
  for ( Tsize_t i = 0; i < theRealLapTime.size(); i ++ ) {
    theRealLapTime[ i ] *= factor;
    theSystemLapTime[ i ] *= factor;
    theUserLapTime[ i ] *= factor;
  }
  return;
}

Tostream& operator<<( Tostream& tos, const TSystemTimer& right )
{
  TSystemTimer copy( right );
  tos << "Real=" << copy.GetRealElapsedTime() << copy.theUnit;
  tos << ",User=" << copy.GetUserElapsedTime() << copy.theUnit;
  tos << ",System=" << copy.GetSystemElapsedTime() << copy.theUnit;
  tos << ",Running=" << copy.GetTotalRunningTime() << copy.theUnit;
  tos << ",Idling=" << copy.GetTotalIdlingTime() << copy.theUnit;

  for ( Tint i = 0; i < copy.GetNumberOfLaps(); i ++ ) {
    tos << Tendl;
    tos << Ttab << "Real[";
    tos << setfill( '0' ) << setiosflags( Tios::right ) << setw( 3 ) << i;
    tos << "]=";
    tos << copy.theRealLapTime[ i ] << copy.theUnit;

    tos << ",User[";
    tos << setfill( '0' ) << setiosflags( Tios::right ) << setw( 3 ) << i;
    tos << "]=";
    tos << copy.theUserLapTime[ i ] << copy.theUnit;

    tos << ",System[";
    tos << setfill( '0' ) << setiosflags( Tios::right ) << setw( 3 ) << i;
    tos << "]=";
    tos << copy.theSystemLapTime[ i ] << copy.theUnit;
  }

  tos << Tflush;
  return tos;
}

Tstring TSystemTimer::WhatTimeIsItNow() const
{
  TSystemClock clock;
  return clock.WhatTimeIsItNow();
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TSystemTimer)
#endif
