{--------------------------------------------------------------------------------------------------

  ApSimpleStowatch
  ================
  This is a simple stopwatch which returns elapsed time in milliseconds or seconds. You also can
  use it as visual component so you do not have to bother about creating or destroying. I think
  there is not much to explain about it ;)

  License
  =======
  Copyright 2010, Armin Pfäffle <mail@armin-pfaeffle.de>

  Released under the MIT license.
  http://www.armin-pfaeffle.de/licenses/mit

  Changelog
  =========
  17.12.2010 - v1.0.0.1
    - Changed fallback counter from GetTickCount() to timeGetTime() (MMSystem unit) for a better
      resolution.
    - Added support and packages for Delphi 7-XE.
    - Fixed some minor bugs.

  16.12.2010 - v1.0.0.0
    - Initial release.

 --------------------------------------------------------------------------------------------------}

unit ApSimpleStopwatch;

interface

uses
  Windows,
  MMSystem,
  Classes;

type
{--------------------------------------------------------------------------------------------------
  This is a simple stopwatch which returns the elapsed time in milliseconds or seconds. If there is
  a high resolution counter (microseconds) in your system then this counter is used
  (QueryPerformanceFrequency, QueryPerformanceCounter) else a counter for milliseconds (timeGetTime,
  MMSystem);
 --------------------------------------------------------------------------------------------------}
TApSimpleStopwatch = class(TComponent)
private
  FHighResolution : Boolean;
  FUseHighResolution : Boolean;
  FFrequency : Int64;
  FRunning : Boolean;
  FStartTimestamp : Int64;
  FElapsed : Int64;
  procedure SetUseHighResolution(Value: Boolean);
  function GetCurrentTimestamp(): Int64;
public
  constructor Create(AOwner: TComponent); override;
  procedure Start();
  procedure Stop();
  procedure Reset();
  function GetElapsedMilliseconds(): Int64;
  function GetElapsedSeconds(): Int64;
  property Running : Boolean read FRunning;
published
  property HighResolution : Boolean read FHighResolution;
  property UseHighResolution : Boolean read FUseHighResolution write SetUseHighResolution default True;
end;

procedure Register;


implementation


{--------------------------------------------------------------------------------------------------
  Registers component TApSimpleStopwatch.
 --------------------------------------------------------------------------------------------------}
procedure Register;
begin
  RegisterComponents('ApUtilities', [TApSimpleStopwatch]);
end;



{ TApSimpleStopwatch }

{--------------------------------------------------------------------------------------------------
  Setter for FUseHighResolution. Resets the counter.
 --------------------------------------------------------------------------------------------------}
procedure TApSimpleStopwatch.SetUseHighResolution(Value: Boolean);
begin
  if (Value <> FUseHighResolution) then
  begin
    FUseHighResolution := Value;
    Reset();
  end;
end;

{--------------------------------------------------------------------------------------------------
  Returns the current timestamp based on FHighResolution.
 --------------------------------------------------------------------------------------------------}
function TApSimpleStopwatch.GetCurrentTimestamp(): Int64;
begin
  if (FHighResolution) and (FUseHighResolution) then
    QueryPerformanceCounter(Result)
  else
    Result := timeGetTime();
end;

{--------------------------------------------------------------------------------------------------
  Default constructor detects of there is a high resolution counter in the system. If not then this
  stopwatch uses the timeGetTime() of the MMSystem unit to calculate the time.
 --------------------------------------------------------------------------------------------------}
constructor TApSimpleStopwatch.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FHighResolution := (QueryPerformanceFrequency(FFrequency));
  FUseHighResolution := True;
  Reset();
end;

{--------------------------------------------------------------------------------------------------
  Starts the stopwatch if not running already.
 --------------------------------------------------------------------------------------------------}
procedure TApSimpleStopwatch.Start();
begin
  if (not FRunning) then
  begin
    FStartTimestamp := GetCurrentTimestamp();
    FRunning := True;
  end;
end;

{--------------------------------------------------------------------------------------------------
  Stops the running stopwatch and calculates the elapsed time.
 --------------------------------------------------------------------------------------------------}
procedure TApSimpleStopwatch.Stop();
begin
  if (FRunning) then
  begin
    FElapsed := FElapsed + (GetCurrentTimestamp() - FStartTimestamp);
    FRunning := False;
  end;
end;

{--------------------------------------------------------------------------------------------------
  Resets the stopwatch which means that it's stopped.
 --------------------------------------------------------------------------------------------------}
procedure TApSimpleStopwatch.Reset();
begin
  FRunning := False;
  FStartTimestamp := 0;
  FElapsed := 0;
end;

{--------------------------------------------------------------------------------------------------
  Returns the elapsed time in milliseconds.
 --------------------------------------------------------------------------------------------------}
function TApSimpleStopwatch.GetElapsedMilliseconds(): Int64;
begin
  Result := FElapsed;
  if (FRunning) then
    Result := Result + (GetCurrentTimestamp() - FStartTimestamp);
  if (FHighResolution) and (FUseHighResolution) then
      Result := (Result * 1000) div FFrequency;
end;

{--------------------------------------------------------------------------------------------------
  Returns the elapsed time in seconds.
 --------------------------------------------------------------------------------------------------}
function TApSimpleStopwatch.GetElapsedSeconds(): Int64;
begin
  Result := GetElapsedMilliseconds() div 1000;
end;


end.

