# -*- coding: iso-8859-1 -*-

# ==============================================================================
# COPYRIGHT (C) 1991 - 2003  EDF R&D                  WWW.CODE-ASTER.ORG
# 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 EDF R&D CODE_ASTER,
#    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
# ==============================================================================

"""Definition of ASTER_TIMER class.
"""

__revision__ = "$Id: as_timer.py 2566 2006-12-08 10:09:20Z courtois $"


# ----- differ messages translation
def _(mesg):
   return mesg

import os
import time

#-------------------------------------------------------------------------------
def _dtimes():
   """Returns a dict of cpu, system and total times.
   """
   l_t = os.times()
   return { 'cpu'   : (l_t[0], l_t[2]),
            'sys'   : (l_t[1], l_t[3]),
            'tot'   : l_t[4], }

#-------------------------------------------------------------------------------
def _conv_hms(t):
   """Convert a number of seconds in hours, minutes, seconds.
   """
   h = int(t/3600)
   m = int(t % 3600)/60
   s = (t % 3600) % 60
   return h, m, s

#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
class ASTER_TIMER:
   """This class provides methods to easily measure time spent during
   different steps.
   Methods :
      Start : start a timer in mode 'INIT' ([re]start from 0) or 'CONT'
         (restart from last value).
      Stop  : stop a timer
   Attributes :
      timers : dict {
         timer_name : {
            'state' : state,
            'cpu_t0' : initial cpu time,
            'cpu_dt' : spent cpu time,
            'sys_t0' : initial system time,
            'sys_dt' : spent system time,
            'tot_t0' : start time,
            'tot_dt' : total spent time,
            'num': timer number (to print timers in order of creation),
         },
         ...
      }
         state is one of 'start', 'stop'
   """
   MaxNumTimer = 9999999
   TotalKey = _('Total time')
   Cpu      = _('cpu')
   System   = _('system')
   Elapsed  = _('total elapsed')

#-------------------------------------------------------------------------------
   def __init__(self, add_total=True, format='s'):
      """Constructor
      """
      # ----- initialisation
      self.timers = {}
      self.total = add_total
      if self.total:
         self.Start(self.TotalKey, num=self.MaxNumTimer)
      self.format = format
      if not format in ('s', 'hms'):
         self.format = 's'

#-------------------------------------------------------------------------------
   def Start(self, timer, mode='CONT', num=None):
      """Start a new timer or restart one
      """
      isnew = not timer in self.timers.keys()
      if not num:
         num = len(self.timers)
      if mode == 'INIT':
         num = self.timers[timer]['num']
      dico = _dtimes()
      if isnew or mode == 'INIT':
         self.timers[timer] = {
            'state'  : 'start',
            'cpu_t0' : dico['cpu'],
            'cpu_dt' : 0.,
            'sys_t0' : dico['sys'],
            'sys_dt' : 0.,
            'tot_t0' : dico['tot'],
            'tot_dt' : 0.,
            'num': num,
         }
      elif mode == 'CONT' and self.timers[timer]['state'] == 'stop':
         self.timers[timer].update({
            'state'  : 'start',
            'cpu_t0' : dico['cpu'],
            'sys_t0' : dico['sys'],
            'tot_t0' : dico['tot'],
         })

#-------------------------------------------------------------------------------
   def Stop(self, timer):
      """Stop in timer
      """
      if not timer in self.timers.keys():
         self.timers[timer] = {
            'state'  : 'stop',
            'cpu_t0' : 0.,
            'cpu_dt' : 0.,
            'sys_t0' : 0.,
            'sys_dt' : 0.,
            'tot_t0' : 0.,
            'tot_dt' : 0.,
            'num': len(self.timers),
         }
      elif self.timers[timer]['state'] == 'start':
         dico = _dtimes()
         self.timers[timer]['state'] = 'stop'
         for i in range(len(dico['cpu'])):
            self.timers[timer]['cpu_dt'] += \
               dico['cpu'][i] - self.timers[timer]['cpu_t0'][i]
         self.timers[timer]['cpu_t0'] = dico['cpu']
         for i in range(len(dico['sys'])):
            self.timers[timer]['sys_dt'] += \
               dico['sys'][i] - self.timers[timer]['sys_t0'][i]
         self.timers[timer]['sys_t0'] = dico['sys']
         self.timers[timer]['tot_dt'] = self.timers[timer]['tot_dt'] + \
               dico['tot'] - self.timers[timer]['tot_t0']
         self.timers[timer]['tot_t0'] = dico['tot']

#-------------------------------------------------------------------------------
   def StopAll(self):
      """Stop all timers
      """
      lk = self.timers.keys()
      if self.total:
         lk.remove(self.TotalKey)
      for timer in lk:
         self.Stop(timer)

#-------------------------------------------------------------------------------
   def __repr__(self):
      """Pretty print content of the timer.
      NB : call automatically StopAll
      """
      self.StopAll()
      self.Stop(self.TotalKey)
      # formats
      label_size = min(80-21, max([len(k) for k in self.timers.keys()]))
      sepa = ' '+'-'*(2+label_size+3+(14+2)*3)
      format = '   %-'+str(label_size)+'s   '
      fmthms = format + '%02dh %02dm %5.2fs  '*3
      fmts   = format + '%12.2f s  '*3
      fmtres = format % '' + '%14s  '*3
      out = []
      # get timers list and sort by 'num'
      lnum = [[val['num'], timer] for timer, val in self.timers.items()]
      lnum.sort()
      if lnum:
         out.append(sepa)
      for num, timer in lnum:
         cpu_dt  = self.timers[timer]['cpu_dt']
         sys_dt  = self.timers[timer]['sys_dt']
         tot_dt  = self.timers[timer]['tot_dt']
         if self.total and num == self.MaxNumTimer and len(lnum)>1:
            out.append(sepa)
         if self.format == 's':
            out.append(fmts % (timer, cpu_dt, sys_dt, tot_dt))
         elif self.format == 'hms':
            h1, m1, s1 = _conv_hms(cpu_dt)
            h2, m2, s2 = _conv_hms(sys_dt)
            h3, m3, s3 = _conv_hms(tot_dt)
            out.append(fmthms % (timer, h1, m1, s1, h2, m2, s2, h3, m3, s3))
         if self.total and num == self.MaxNumTimer:
            out.append(fmtres % tuple(\
                  ['(%s)' % s for s in (self.Cpu, self.System, self.Elapsed)]))
      if lnum:
         out.append(sepa)
      return os.linesep.join(out)

#-------------------------------------------------------------------------------
if __name__ == '__main__':
   chrono = ASTER_TIMER(format='s')
   chrono.Start('Build Code_Aster')
   chrono.Start('Compilation')
   #time.sleep(0.4)
   print os.times()
   for i in range(100000):
      z=5.**2
      txt=open('as_timer.py', 'r').read()
   chrono.Stop('Compilation')
   chrono.Start('Child')
   print os.times()
   from popen2 import Popen4
   p = Popen4('ls -R /opt/aster/NEW8')
   p.tochild.close()
   p.fromchild.close()
   iret = p.wait()
   chrono.Stop('Child')
   chrono.Stop('Catalogues')
   chrono.Start('Checkup')
   print os.times()
   for i in range(10000):
      txt=open('as_timer.py', 'r').read()
   chrono.Stop('Checkup')
   time.sleep(0.3)
   chrono.Start('Compilation')
   #time.sleep(1.1)
   print os.times()
   for i in range(20000):
      txt=open('as_timer.py', 'r').read()
   print os.times()
   chrono.Stop('Compilation')
   print chrono
   print 'Expecting :'
   print 'build : 3.6'
   print 'compilation : 2.5'
   print 'catalogues : 0.'
