{$MODESWITCH AUTODEREF}
unit DMetrics;

interface

uses
  dstrnum,
  dtypes,
  DEngBase;

type
PMetrics = ^TMetrics;

// 0..9,
// 20..100,
// 200..1000,
// >1000
THistogram = array[0 .. 10 + 9 + 9 + 1] of LongInt;

TMetrics = object
private
  FPrintToLog: Boolean;
public
  constructor Init;
  destructor Done; virtual;
  procedure Send(const Name: AnsiString; Value: LongInt); overload;
  function Time: LongInt; inline;
end;

// TODO use Advanced record instead of object
TMetricDuration = object
private
  FMetrics: PMetrics;
  FName: AnsiString;
  FStart: LongInt;
public
  constructor Init(Metrics: PMetrics; const Name: AnsiString); overload;
  constructor Init(const Name: AnsiString); overload;
  destructor Done; virtual;
end;

procedure HistogramReset(var H: THistogram);
procedure HistogramAdd(var H: THistogram; V: LongInt);
procedure HistogramAddDuration(var H: THistogram; var Start: LongInt; Current: LongInt); inline;
function HistogramToString(constref H: THistogram): AnsiString;

implementation

uses
  DEng;

constructor TMetrics.Init;
begin
  FPrintToLog := False;
end;

destructor TMetrics.Done;
begin
end;

procedure TMetrics.Send(const Name: AnsiString; Value: LongInt); overload;
begin
  if FPrintToLog then
    Eng.PrintLogTagged('M', Name + ' ' + IntToStr(Value));
end;

function TMetrics.Time: LongInt;
begin
  Result := Eng.Time;
end;

constructor TMetricDuration.Init(Metrics: PMetrics; const Name: AnsiString);
begin
  FMetrics := Metrics;
  FName := Name;
  FStart := FMetrics.Time;
end;

constructor TMetricDuration.Init(const Name: AnsiString);
begin
  Init(Eng.Metrics, Name);
end;

destructor TMetricDuration.Done;
begin
  FMetrics.Send(FName, FMetrics.Time - FStart);
end;

procedure HistogramReset(var H: THistogram);
var
  I: LongInt;
begin
  for I := 0 to High(H) do
    H[I] := 0;
end;

procedure HistogramAdd(var H: THistogram; V: LongInt);
begin
  if V <= 9 then begin
    Inc(H[V]);
  end else if V <= 100 then begin
    Inc(H[9 + V div 10]);
  end else if V <= 1000 then begin
    Inc(H[18 + V div 100]);
  end else
    Inc(H[High(H)]);
end;

procedure HistogramAddDuration(var H: THistogram; var Start: LongInt; Current: LongInt);
begin
  Dec(Current, Start);
  Inc(Start, Current);
  HistogramAdd(H, Current);
end;

function HistogramToString(constref H: THistogram): AnsiString;
var
  I: LongInt;
begin
  Result := '[';
  for I := 0 to High(H) - 1 do begin
    Result := Result + IntToStr(H[I]) + ',';
  end;
  Result := Result + IntToStr(H[High(H)]) + ']';
end;

end.
