-- File: PupNameServer.mesa,  Last Edit: HGM March 2, 1981  9:41 PM
-- Please don't forget to update the herald too.....

DIRECTORY
  Process USING [Yield],
  Time USING [Current],

  Format USING [], -- Needed by Put.Date and such
  FormSW USING [
    ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, Display,
    FindItem, CommandItem],
  Menu USING [Create, Handle, Instantiate, ItemObject, MCRType],
  Put USING [Char, CR, Date, Decimal, Line, LongNumber, Number, Text],
  Tool USING [Create, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc],
  ToolWindow USING [TransitionProcType],
  UserInput USING [GetDefaultWindow, userAbort],
  Window USING [Handle],

  NameServerDefs USING [
    CacheEntry, EnumerateCache, msg, nameRunning, probing, PupDirServerOn,
    PupNameServerOff, PupNameServerOn, sending, StartProbingForDirectory,
    statVers, statSend, statName, statAddress, statConst, statBusy, statHits,
    statMisses, statNone, statFile, statMsScanningFile],
  PupDefs USING [AppendPupAddress],
  StatsDefs USING [StatGetCounter];

PupNameServerTool: PROGRAM
  IMPORTS
    Process, Time, FormSW, Menu, Put, Tool, UserInput, NameServerDefs, PupDefs,
    StatsDefs
  EXPORTS NameServerDefs =
  BEGIN

  form, log: PUBLIC Window.Handle ← NIL;

  Init: PROCEDURE =
    BEGIN
    [] ← Tool.Create[
      name: "Name Server of March 2, 1981"L, makeSWsProc: MakeSWs,
      clientTransition: ClientTransition, initialState: inactive];
    Menu.Instantiate[menu, UserInput.GetDefaultWindow[]];
    END;

  UpdatePicture: PUBLIC PROCEDURE =
    BEGIN
    IF form = NIL THEN RETURN;
    FormSW.FindItem[form, startIX].flags.invisible ← NameServerDefs.nameRunning;
    FormSW.FindItem[form, stopIX].flags.invisible ← ~NameServerDefs.nameRunning;
    FormSW.Display[form];
    END;

  PrintNameInfo: PROCEDURE [wh: Window.Handle] =
    BEGIN OPEN NameServerDefs, StatsDefs;
    PrintHeader[wh, "Name Server Statistics:"L];
    PrintMaybe[
      wh, "Directory Version requests"L, StatGetCounter[statVers]];
    PrintMaybe[wh, "Directories sent"L, StatGetCounter[statSend]];
    PrintMaybe[wh, "Name Lookup requests"L, StatGetCounter[statName]];
    PrintMaybe[
      wh, "Address Lookup requests"L, StatGetCounter[statAddress]];
    PrintMaybe[wh, "Name Lookup constants"L, StatGetCounter[statConst]];
    PrintMaybe[
      wh, "Requests while Name/Address Lookup server was Busy"L,
      StatGetCounter[statBusy]];
    PrintMaybe[
      wh, "Hits in Name Lookup cache"L, StatGetCounter[statHits]];
    PrintMaybe[
      wh, "Misses found in Name Lookup cache"L, StatGetCounter[
      statMisses]];
    PrintMaybe[
      wh, "Name Lookup cache misses"L, StatGetCounter[statNone]];
    PrintMaybe[
      wh, "Name Lookup file searches"L, StatGetCounter[statFile]];
    IF StatGetCounter[statFile]#0 THEN PrintMaybe[
      wh, "Average time (ms) to search file"L,
      StatGetCounter[statMsScanningFile]/StatGetCounter[statFile]];
    END;

  PrintMaybe: PROCEDURE [wh: Window.Handle, s: STRING, n: LONG INTEGER] =
    BEGIN
    IF n = 0 THEN RETURN;
    Put.Text[wh, s];
    Put.Text[wh, ": "L];
    Put.LongNumber[wh, n, [10, FALSE, TRUE, 0]];
    Put.CR[wh];
    END;

  PrintNameServerCache: PROCEDURE [wh: Window.Handle] =
    BEGIN
    size: CARDINAL ← 0;
    PrintOne: PROCEDURE [ce: NameServerDefs.CacheEntry] =
      BEGIN
      size ← size + ce.size;
      IF UserInput.userAbort THEN RETURN;
      Put.LongNumber[wh, ce.count, [10, FALSE, TRUE, 6]];
      Put.Number[wh, ce.sequence, [10, FALSE, TRUE, 5]];
      Put.Number[wh, ce.size, [10, FALSE, TRUE, 5]];
      Put.Text[wh, "  "L];
      IF LENGTH[ce.names] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO
	  IF i # 0 THEN Put.Text[wh, ", "L]; Put.Text[wh, ce.names[i]]; ENDLOOP;
      Put.Text[wh, " <=> "L];
      IF LENGTH[ce.addrs] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO
	  text: STRING = [30];
	  IF i # 0 THEN Put.Text[wh, ", "L];
	  text.length ← 0;
	  PupDefs.AppendPupAddress[text, ce.addrs[i]];
	  Put.Text[wh, text];
	  ENDLOOP;
      Put.CR[wh];
      DoSomeYields[];
      END;
    PrintHeader[wh, "Cache for Name Lookup Server:"L];
    Put.Line[wh, " Count  Seq  size Name(s) <=> Address(es)"L];
    NameServerDefs.EnumerateCache[PrintOne];
    IF size = 0 THEN Put.Line[wh, "The cache is empty."L]
    ELSE
      BEGIN
      Put.Text[wh, "There are "L];
      Put.Decimal[wh, size];
      Put.Line[wh, " words in use by the cache."L];
      END;
    END;

  PrintHeader: PROCEDURE [wh: Window.Handle, s: STRING] =
    BEGIN
    Put.CR[wh];
    Put.Date[wh, Time.Current[], dateTime];
    Put.Text[wh, "  "L];
    Put.Line[wh, s];
    END;

  DoSomeYields: PROCEDURE =
    BEGIN THROUGH [0..100) DO Process.Yield[]; ENDLOOP; END;

  Start: FormSW.ProcType = BEGIN NameServerDefs.PupNameServerOn[]; END;

  Stop: FormSW.ProcType = BEGIN NameServerDefs.PupNameServerOff[]; END;

  Probe: FormSW.ProcType =
    BEGIN OPEN NameServerDefs;
    SELECT TRUE FROM
      probing => Put.Line[msg, "Already probing for a new directory."L];
      sending => Put.Line[msg, "Currently sending the directory."L];
      ENDCASE => StartProbingForDirectory[];
    END;

  PrintInfo: FormSW.ProcType = BEGIN PrintNameInfo[log]; END;

  PrintCache: FormSW.ProcType = BEGIN PrintNameServerCache[log]; END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    NameServerDefs.msg ← Tool.MakeMsgSW[window: window];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    log ← Tool.MakeFileSW[window: window, name: "NameServer.log$"L];
    Menu.Instantiate[menu, log];
    END;

  menu: Menu.Handle = Menu.Create[DESCRIPTOR[items], "Names", TRUE];
  items: ARRAY [0..1] OF Menu.ItemObject ← [["Info", DoMenu], ["Cache", DoMenu]];

  infoMx: CARDINAL = 0;
  cacheMx: CARDINAL = 1;
  DoMenu: Menu.MCRType =
    BEGIN
    IF window # log THEN window ← NIL; -- Great window in the sky
    SELECT index FROM
      infoMx => PrintNameInfo[window];
      cacheMx => PrintNameServerCache[window];
      ENDCASE => ERROR;
    END;

  startIX: CARDINAL = 0;
  stopIX: CARDINAL = 1;
  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 5;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "Start"L, proc: Start, place: FormSW.newLine,
      invisible: NameServerDefs.nameRunning];
    items[1] ← FormSW.CommandItem[
      tag: "Stop"L, proc: Stop, place: FormSW.newLine,
      invisible: ~NameServerDefs.nameRunning];
    items[2] ← FormSW.CommandItem[tag: "Probe"L, proc: Probe];
    items[3] ← FormSW.CommandItem[tag: "Info"L, proc: PrintInfo];
    items[4] ← FormSW.CommandItem[tag: "Cache"L, proc: PrintCache];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN IF new = inactive THEN NameServerDefs.msg ← form ← log ← NIL; END;

  Init[];
  NameServerDefs.PupNameServerOn[];
  NameServerDefs.PupDirServerOn[];
  END.