{*******************************************************************************
* Crossplatform Timing class intended for measuring time on Linux and Windows  *
*           CROSSPLATFORM EDITION                                              *
*                                                                              *
*           Copyright (C) 2002  Artur Baæ   All Rights Reserved.               *
*           Reda Poland                                                        *
*           arturbac@poczta.onet.pl                                            *
*           version 1.0                                                        *
*           Writen with Kylix2 Open Edition & Delphi6 Open Edition             *
*           ------------------------------------------------------------       *
*   Piece of part of windows code is written on the base of source CGLib       *
*   by Tom Nuygen                                                              *
*******************************************************************************}

unit IGLTime;

interface
uses Sysutils,
{$ifdef win32}
Dialogs;
{$else}
QForms,QExtCtrls,Libc;
{$endif}

{$ifdef win32}
//part of windows code is based on Tom Nuygens cgUtils
function QueryPerformanceCounter(lpPerformanceCount: PComp): Boolean; stdcall; external 'KERNEL32.DLL';
function QueryPerformanceFrequency(lpFrequency: PComp): Boolean; stdcall; external 'KERNEL32.DLL';
{$endif}

{$ifdef win32}
{Becouse windows dosn't have Libc we have to implement few things}
Const
        CLOCKS_PER_SEC    = 1000000;
type
    __time_t        = longint;
    __suseconds_t   = longint;

timeval = {packed} record
    tv_sec: __time_t;           { Seconds.  }
    tv_usec: __suseconds_t;     { Microseconds. 10^-6 sec }
end;
{$endif}

{ Time used by the program so far (user time + system time).
   The result / CLOCKS_PER_SECOND is program time in seconds.
    extern clock_t clock (void) __THROW;
 ISO/IEC 9899:1990 7.12.1: <time.h>
   The macro `CLOCKS_PER_SEC' is the number per second of the value
   returned by the `clock' function.
 CAE XSH, Issue 4, Version 2: <time.h>
   The value of CLOCKS_PER_SEC is required to be 1 million on all
   XSI-conformant systems.

 Even though CLOCKS_PER_SEC has such a strange value CLK_TCK
   presents the real value for clock ticks per second for the system.
 int gettimeofday (struct timeval *tp, struct timezone *tzp)           }

Type
    uint = cardinal;

IGLTimer = class (TObject)
    Private
        tseterror   : boolean; //this will prevent us to see 10000 of windows with error reports
        {$ifdef linux}
            starttime : timeval;
            endtime  : timeval;
        {$else}
            winstarttime : comp;
            winendtime : comp;
        {$endif}

        //startcpu    : double;
        //endcpu  : double;
        //cpumess,
        mess : ansistring ;

        Function GetTimeElapsed : timeval;
        Function GetSecElapsed  : int64;
        Function GetUSecElapsed  : int64;
        Procedure TimeError(txterror : ansistring;messtype : integer);
    Public
        Constructor Create;
        Procedure Start;
        Procedure Stop;
        //Procedure Continue;
        Procedure Reset;

        Property ErrorMessage : ansistring Read mess;
        //Property ErrorCPUMessage : ansistring Read cpumess;

        Property TimeElapsed    : timeval Read GetTimeElapsed;
        Property SecElapsed     : int64 Read GetSecElapsed;
        Property USecElapsed    : int64 Read GetUSecElapsed;

end;

implementation
Const
    CPUMESSERROR    = 0;
    MESSERROR       = 1;
var
{$ifdef linux}
  clear : timeval;
{$else}
  freq : comp;
{$endif}
        Constructor IGLTimer.Create;
        Begin
            {$ifdef win32}
            QueryPerformanceFrequency(@freq);

            if freq >0 then
                mess:='Timer initialized ok'
            else
                TimeError('Result error: Results that depends on time are incorect',MESSERROR);
            {$else}
                mess:='Timer initialized ok'
            {$endif}


            //cpumess:='CPUTimer ok';
        End;
        Function IGLTimer.GetTimeElapsed : timeval;
            Begin
                if USecElapsed>1 then begin
                    {$ifdef linux}
                    //if (endtime.tv_sec=0 and endtime.tv_usec=0) then Stop;
                    result.tv_sec:=endtime.tv_sec-starttime.tv_sec;
                    result.tv_usec:=endtime.tv_usec-starttime.tv_usec;
                    {$else}
                    if winendtime =0 then Stop;
                    result.tv_sec:= trunc((winendtime-winstarttime)/freq);
                    result.tv_usec:=trunc((winendtime-winstarttime)*1000000/freq)-result.tv_sec*1000000;

                    {$endif}


//           Result.tv_usec:= ntp_starttime.time.tv_usec - ntp_endtime.time.tv_usec;
                end;
            End;
        Function IGLTimer.GetSecElapsed  : int64;
            Begin
                {$ifdef linux}
                    Result:=(endtime.tv_sec-starttime.tv_sec)+trunc((endtime.tv_usec-starttime.tv_usec)/1000000);
                {$else}
                    Result:=trunc((winendtime-winstarttime) / freq);
                {$endif}

                    if result<0 then begin
                        TimeError('Result error: Results that depends on time are incorect',MESSERROR);
                        result:=0;
                    end;

            End;
        Function IGLTimer.GetUSecElapsed  : int64;
            Begin

                //result is int64 so max value is 2^64 = 1.844674407^19
                {$ifdef linux}
                    Result:=(endtime.tv_sec-starttime.tv_sec)*1000000+(endtime.tv_usec-starttime.tv_usec);
                {$else}
                    Result:=trunc((winendtime-winstarttime)*1000000/ freq);
                {$endif}
                    if result<1 then begin
                        TimeError('Result error: Results that depends on time are incorect',MESSERROR);
                        result:=1;
                    end;
            End;

        Procedure IGLTimer.Start;
            Begin
                Reset;
                {$ifdef linux}
                    //startcpu:= clock / CLOCKS_PER_SEC;
                    if (gettimeofday(starttime,nil) ) <0 then
                        TimeError('Clock error: Results that depends on time are incorect',MESSERROR);
                    //if startcpu<0 then
                        //TimeError('CPUClock error results dependet on time are incorect',CPUMESSERROR);
                {$else}
                    if not(QueryPerformanceCounter(@winstarttime))then
                                TimeError('Clock error results dependet on time are incorect',MESSERROR);
                  //Deployed result is 1000/s
                {$endif}
            End;
        Procedure IGLTimer.Stop;
            Begin
                {$ifdef linux}
                    //endcpu:= clock / CLOCKS_PER_SEC;
                    if (gettimeofday (endtime,nil) )<0 then
                        TimeError('Clock error: Results that depends on time are incorect',MESSERROR);
                    //if endcpu<0 then TimeError('CPUClock error results dependet on time are incorect',CPUMESSERROR);
                {$else}
                    if not(QueryPerformanceCounter(@winendtime))then
                        TimeError('Clock error: Results that depends on time are incorect',MESSERROR);
                {$endif}
            End;
        Procedure IGLTimer.Reset;
            Begin
                {$ifdef linux}
                    starttime:=clear;
                    endtime:=clear;
                {$else}
                    winendtime:=0;
                    winstarttime:=0;
                {$endif}

            End;
        Procedure IGLTimer.TimeError(txterror : ansistring;messtype : integer);
        Begin
            if tseterror = false then begin
                tseterror:=true;
                {$ifdef win32}
                MessageDlg( 'The precision clock model is not properly set up at the moment, thus the clock must be considered unsynchronized, and the values should be treated with care.',
                               mtWarning,[mbOk], 0);
                {$else}
                Application.MessageBox('The precision clock model is not properly set up at the moment, thus the clock must be considered unsynchronized, and the values should be treated with care.',
                                            pchar(txterror),[smbOK],smsWarning);
                {$endif}
                //if messtype = CPUMESSERROR then
                    //cpumess:=txterror
                //else
                    mess:=txterror;
            end;
        End;
  end.

