-- File: PupEchoServer.mesa - last edit:
-- AOF                  3-Feb-88 11:33:27
-- WIrish               5-Feb-88 12:42:58
-- HGM                 25-Jun-85  1:13:07
-- Copyright (C) 1983, 1985, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Ascii USING [CR],
  Display USING [Bitmap, Invert, replaceFlags, White],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  FormSW USING [
    AllocateItemDescriptor, newLine, ClientItemsProcType, ProcType, FindItem,
    Display, CommandItem, BooleanItem],
  MsgSW USING [AppendString],
  Process USING [Yield],
  Runtime USING [GetBcdTime],
  String USING [AppendString],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Time USING [Append, Unpack],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW, AddThisSW],
  ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType],
  Window USING [Handle, Box],
  PupWireFormat USING [MesaToBcplLongNumber],
  PupEchoServerDefs USING [
    echoStatsRequest, echoStatsReply, EchoStatsEntry, echoVersion],
  Stats USING [StatIncr, StatBump, StatGetCounter],
  PupDefs USING [
    PupBuffer, PupPackageMake, PupPackageDestroy, ReturnBuffer,
    PupRouterSendThis, ReturnPup, SwapPupSourceAndDest, GetPupContentsBytes,
    PupSocket, PupSocketMake, PupSocketDestroy, PupSocketKick, veryLongWait],
  PupTypes USING [echoSoc, fillInPupAddress];

PupEchoServer: PROGRAM
  IMPORTS
    Display, Event, FormSW, MsgSW, Runtime, String, Supervisor, Time, Process, Tool, ToolWindow,
    PupWireFormat, Stats, PupDefs
  EXPORTS PupEchoServerDefs =
  BEGIN OPEN PupDefs;

  useCount, hits: CARDINAL ← 0;
  pleaseStop, running, verbose: BOOLEAN ← FALSE;
  echoFork: PROCESS;
  echoSocket: PupSocket;
  showSomething: PROCEDURE [CHARACTER] ← DummyEchoHook;

  tool, msg, form, boxes: Window.Handle ← NIL;
  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];

  EchoServerOn: PUBLIC PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN
      BEGIN
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      running ← TRUE;
      Starter[];
      END;
    UpdatePicture[];
    END;

  Starter: PROCEDURE =
    BEGIN
    pleaseStop ← FALSE;
    [] ← PupDefs.PupPackageMake[];
    echoSocket ← PupSocketMake[
      PupTypes.echoSoc, PupTypes.fillInPupAddress, veryLongWait];
    echoFork ← FORK Echoer[];
    END;

  EchoServerOff: PUBLIC PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN
      running ← FALSE;
      Stopper[];
      Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
      END;
    UpdatePicture[];
    END;

  Stopper: PROCEDURE =
    BEGIN
    pleaseStop ← TRUE;
    PupSocketKick[echoSocket];
    JOIN echoFork[];
    PupSocketDestroy[echoSocket];
    PupDefs.PupPackageDestroy[];
    END;

  UpdatePicture: PROCEDURE =
    BEGIN
    IF running THEN SetupBoxes[] ELSE SetDownBoxes[];
    IF form = NIL THEN RETURN;
    FormSW.FindItem[form, startIX].flags.invisible ← running;
    FormSW.FindItem[form, stopIX].flags.invisible ← ~running;
    FormSW.Display[form];
    END;

  Echoer: PROCEDURE =
    BEGIN
    b: PupBuffer;
    UNTIL pleaseStop DO
      IF (b ← echoSocket.get[]) # NIL THEN
        BEGIN
        SELECT b.pup.pupType FROM
          echoMe =>
            BEGIN
            Stats.StatIncr[pupsEchoed];
            Stats.StatBump[pupBytesEchoed, GetPupContentsBytes[b]];
            b.pup.pupType ← iAmEcho;
            SwapPupSourceAndDest[b];
            PupRouterSendThis[b];
            showSomething['!];
            END;
          PupEchoServerDefs.echoStatsRequest =>
            BEGIN OPEN PupEchoServerDefs;
            ese: LONG POINTER TO EchoStatsEntry ← LOOPHOLE[@b.pup.pupWords];
            ese↑ ← [
              version: echoVersion,
              pupsEchoed: PupWireFormat.MesaToBcplLongNumber[
              Stats.StatGetCounter[pupsEchoed]]];
            ReturnPup[
              b, PupEchoServerDefs.echoStatsReply, 2*SIZE[EchoStatsEntry]];
            END;
          ENDCASE => ReturnBuffer[b];
        END;
      Process.Yield[];  -- avoid hogging machine
      ENDLOOP;
    END;

  DummyEchoHook: PUBLIC PROCEDURE [c: CHARACTER] = {};

  RealEchoHook: PUBLIC PROCEDURE [c: CHARACTER] =
    BEGIN
    s: STRING = [2];
    IF boxes # NIL THEN FlipBoxes[];
    IF msg = NIL OR ~verbose THEN RETURN;
    s[0] ← c;
    s.length ← 1;
    IF ((hits ← hits + 1) MOD 50) = 0 THEN {s[1] ← Ascii.CR; s.length ← 2; };
    MsgSW.AppendString[msg, s];
    END;

  indicator: {off, left, right} ← off;
  indicatorBox: Window.Box = [[25, 10], [16, 16]];
  DisplayBoxes: ToolWindow.DisplayProcType =
    BEGIN
    pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;
    left: WORD = 177400B;
    right: WORD = 000377B;
    SELECT indicator FROM
      left => pattern ← [ALL[left], ALL[right]];
      right => pattern ← [ALL[right], ALL[left]];
      off => pattern ← [ALL[0], ALL[0]];
      ENDCASE;
    Display.Bitmap[window, indicatorBox, [@pattern, 0, 0], 16, Display.replaceFlags]
    END;

  SetupBoxes: PROCEDURE =
    BEGIN indicator ← left; IF boxes # NIL THEN DisplayBoxes[boxes]; END;

  FlipBoxes: PROCEDURE =
    BEGIN
    SELECT indicator FROM
      left => indicator ← right;
      off, right => indicator ← left;
      ENDCASE;
    IF boxes # NIL THEN Display.Invert[boxes, indicatorBox];
    END;

  SetDownBoxes: PROCEDURE =
    BEGIN
    indicator ← off;
    IF boxes # NIL THEN Display.White[boxes, indicatorBox];
    END;

  MakeBoxesSW: PROCEDURE [window: Window.Handle] =
    BEGIN
    box: Window.Box ← ToolWindow.nullBox;
    box.dims.h ← 36;
    boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes, box: box];
    Tool.AddThisSW[window: window, sw: boxes, swType: vanilla];
    END;

  Start: FormSW.ProcType = BEGIN EchoServerOn[]; END;

  Stop: FormSW.ProcType = BEGIN EchoServerOff[]; END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    msg ← Tool.MakeMsgSW[window: window, lines: 3];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    MakeBoxesSW[window];
    END;

  startIX: CARDINAL = 0;
  stopIX: CARDINAL = 1;
  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 3;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "Start"L, proc: Start, place: FormSW.newLine, invisible: running];
    items[1] ← FormSW.CommandItem[
      tag: "Stop"L, proc: Stop, place: FormSW.newLine, invisible: ~running];
    items[2] ← FormSW.BooleanItem[tag: "Verbose"L, switch: @verbose];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    IF new = inactive THEN msg ← form ← boxes ← NIL;
    showSomething ← IF new = active THEN RealEchoHook ELSE DummyEchoHook;
    END;

  Broom: Supervisor.AgentProcedure =
    BEGIN
    SELECT event FROM
      EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume =>
        IF running THEN Stopper[];
      ENDCASE => NULL;
    END;


  Init: PROCEDURE =
    BEGIN
    herald: LONG STRING = [100];
    String.AppendString[herald, "Pup EchoServer of "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    tool ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs,
      clientTransition: ClientTransition, initialState: inactive];
    END;
  
  Init[];
  EchoServerOn[];  -- This may be undesirable
  END.