-- File: StatsCold.mesa,  Last Edit: January 7, 1981  1:20 PM

DIRECTORY
  Process USING [Detach, SetTimeout, SecondsToTicks],
  Put USING [CR, Char, Text, Line],
  System USING [
    GreenwichMeanTime, GetGreenwichMeanTime, Pulses, GetClockPulses, Microseconds,
    PulsesToMicroseconds],
  Time USING [AppendCurrent],
  Window USING [Handle],
  StatsDefs USING [StatBump, StatCounterIndex],
  StatsOps USING [statGrand, StatPrintCounters, StatsPrint, StatsStrings];

StatsCold: MONITOR LOCKS statLock
  IMPORTS Process, Put, System, Time, StatsDefs, StatsOps
  EXPORTS StatsDefs, StatsOps =
  BEGIN OPEN StatsDefs, StatsOps;

  statLock: PUBLIC MONITORLOCK;

  initDateAndTime: STRING ← [18]; -- when we were initialized

  grand: ARRAY StatCounterIndex OF LONG CARDINAL;

  StatsGetCounters: PUBLIC PROCEDURE
    RETURNS [POINTER TO ARRAY StatCounterIndex OF LONG CARDINAL] =
    BEGIN RETURN[@grand]; END;

  StatUpdater: ENTRY PROCEDURE =
    BEGIN
    updater: CONDITION;
    Process.SetTimeout[@updater, Process.SecondsToTicks[15*60]];
    DO
      -- forever
      WAIT updater;
      StatUpdateLocked[];
      ENDLOOP;
    END;

  StatUpdate: PUBLIC ENTRY PROCEDURE = BEGIN StatUpdateLocked[]; END;

  -- Update various things.

  --  It can be called anytime, but must be called "often enough".  The 39ms clock overflows every 41 min, so be sure to call it more often than that.  StatsCold FORKs to a PROCESS that calls it every 20 minutes.

  --  Also, it should be called before looking at grand[StatTime or statSeconds], as in when printing things out.

  --  One idea to make things go faster is to actually count things in 16 bit mode, and call somebody before the small counters overflow to copy the info out and reset them.  This is where that should get done, but its a hard problem because there are no MESA instructions that do an atomic read and reset.

  -- our copies of the time sampled when we last looked at the clock

  oldSeconds: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
  oldPulses: System.Pulses ← System.GetClockPulses[];
  -- Time may go backwards when it gets reset.
  spareSeconds: LONG INTEGER ← 0; -- remembers fractions of an hour

  StatUpdateLocked: INTERNAL PROCEDURE =
    BEGIN
    nowPulses: System.Pulses ← System.GetClockPulses[];
    recent: LONG CARDINAL;
    delta: System.Microseconds;
    newSeconds: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    delta ← System.PulsesToMicroseconds[System.Pulses[nowPulses - oldPulses]];
    grand[statTime] ← grand[statTime] + delta/1000;
    oldPulses ← nowPulses;
    recent ← newSeconds - oldSeconds;
    oldSeconds ← newSeconds;
    grand[statSeconds] ← grand[statSeconds] + recent;
    spareSeconds ← spareSeconds + recent;
    UNTIL spareSeconds < 3600 DO
      spareSeconds ← spareSeconds - 3600;
      grand[statHours] ← grand[statHours] + 1;
      ENDLOOP;
    END;

  -- remember current date and time for header lines

  StatNew: PUBLIC ENTRY PROCEDURE =
    BEGIN
    initDateAndTime.length ← 0;
    Time.AppendCurrent[initDateAndTime];
    StatZap[];
    END;


  -- reset counters, and print a here-we-go line

  StatStart: PUBLIC ENTRY PROCEDURE [wh: Window.Handle, header: STRING] =
    BEGIN
    StatZap[];
    StatPrintDateAndTime[wh];
    Put.Text[wh, header];
    IF initDateAndTime.length # 0 THEN
      BEGIN Put.Text[wh, " of "L]; Put.Text[wh, initDateAndTime]; END;
    Put.Line[wh, "."L];
    END;


  -- print out current numbers

  StatPrintCurrent: PUBLIC ENTRY PROCEDURE [wh: Window.Handle] =
    BEGIN
    StatUpdateLocked[];
    StatPrintDateAndTime[wh];
    Put.Line[wh, "Current counters."L];
    StatPrintCounters[wh, @grand];
    Put.CR[wh];
    END;


  -- print out current numbers

  StatFinish: PUBLIC ENTRY PROCEDURE [wh: Window.Handle] =
    BEGIN
    StatUpdateLocked[];
    StatPrintDateAndTime[wh];
    Put.Line[wh, "Grand Totals."L];
    StatPrintCounters[wh, @grand];
    Put.CR[wh];
    StatZap[]; -- just in case

    END;


  -- reset the world to be nice and clean again

  StatZapit: ENTRY PROCEDURE = BEGIN StatZap[]; END;

  StatZap: INTERNAL PROCEDURE =
    BEGIN
    StatUpdateLocked[]; -- init most internal stuff
    spareSeconds ← 0;
    recent ← grand ← ALL[0];
    END;

  -- our copy of grand at last call to StatSince or StatReady

  recent: ARRAY StatCounterIndex OF LONG CARDINAL;


  -- setup things for StatSince or StatSummary
  StatReady: PUBLIC ENTRY PROCEDURE =
    BEGIN StatUpdateLocked[]; recent ← grand; END;

  -- print out new numbers since last call to StatReady or StatSince

  StatSince: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    temp: ARRAY StatCounterIndex OF LONG CARDINAL;
    GetCopies: ENTRY PROCEDURE = INLINE
      BEGIN StatUpdateLocked[]; temp ← grand; END;
    GetCopies[];
    FOR i: StatCounterIndex IN StatCounterIndex DO
      recent[i] ← temp[i] - recent[i]; ENDLOOP;
    StatPrintDateAndTime[wh];
    Put.Line[wh, "Recent Statistics."L];
    StatPrintCounters[wh, @recent];
    Put.CR[wh];
    recent ← temp;
    END;

  -- AppendCurrent has format of dd-mmm-yy hh:mm:ss

  StatPrintDateAndTime: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    string: STRING ← [18];
    --Toolies don't know how to do this (yet?)!  IF ~NewLine[] THEN Put.CR[wh];
    Time.AppendCurrent[string];
    Put.Text[wh, string];
    Put.Char[wh, ' ];
    Put.Char[wh, ' ];
    END;


  -- initialization

  StatsDefs.StatBump[statMouseTrap, 0];
  -- StatsHot isn't part of our config in Pilot
  StatsOps.statGrand ← @grand;
  START StatsOps.StatsPrint;
  START StatsOps.StatsStrings;
  StatZapit[];
  Process.Detach[FORK StatUpdater[]];
  END.