-- File: EchoServer.mesa,  Last Edit: January 30, 1981  3:16 PM
-- Please don't forget to update the herald too.....

DIRECTORY
  Ascii USING [CR],
  InlineDefs USING [MesaToBcplLongNumber],
  Process USING [Yield],

  Event USING [Item, Reason, AddNotifier],
  FormSW USING [
    AllocateItemDescriptor, newLine, ClientItemsProcType, ProcType, FindItem,
    Display, CommandItem, BooleanItem],
  MsgSW USING [AppendString],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW, AddThisSW],
  ToolWindow USING [TransitionProcType, DisplayProcType, CreateSubwindow],
  Window USING [Handle, Box, DisplayData, DisplayInvert, DisplayWhite],

  EchoServerDefs USING [
    echoStatsRequest, echoStatsReply, EchoStatsEntry, echoVersion],
  StatsDefs USING [StatIncr, StatBump, StatGetCounter],
  PupDefs USING [
    PupBuffer, ReturnFreePupBuffer, PupPackageMake, PupPackageDestroy,
    PupRouterSendThis, ReturnPup, SwapPupSourceAndDest, GetPupContentsBytes,
    PupSocket, PupSocketMake, PupSocketDestroy, PupSocketKick, veryLongWait],
  PupTypes USING [echoSoc, fillInPupAddress];

EchoServer: PROGRAM
  IMPORTS
    InlineDefs, Process, Event, FormSW, MsgSW, Tool, ToolWindow, Window,
    StatsDefs, PupDefs
  EXPORTS EchoServerDefs =
  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;
  eventItem: Event.Item ← [eventMask: 177777B, eventProc: Broom];

  EchoServerOn: PUBLIC PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN BEGIN 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[]; 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.pupType FROM
	  echoMe =>
	    BEGIN
	    b.pupType ← iAmEcho;
	    SwapPupSourceAndDest[b];
	    PupRouterSendThis[b];
	    StatsDefs.StatIncr[pupsEchoed];
	    StatsDefs.StatBump[pupBytesEchoed, GetPupContentsBytes[b]];
	    showSomething['!];
	    END;
	  EchoServerDefs.echoStatsRequest =>
	    BEGIN OPEN EchoServerDefs;
	    ese: LONG POINTER TO EchoStatsEntry ← LOOPHOLE[@b.pupWords];
	    ese↑ ←
	      [version: echoVersion,
		pupsEchoed: InlineDefs.MesaToBcplLongNumber[
		StatsDefs.StatGetCounter[pupsEchoed]]];
	    ReturnPup[b, EchoServerDefs.echoStatsReply, 2*SIZE[EchoStatsEntry]];
	    END;
	  ENDCASE => ReturnFreePupBuffer[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;
    Window.DisplayData[window, indicatorBox, @pattern, 1]
    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 Window.DisplayInvert[boxes, indicatorBox];
    END;

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

  MakeBoxesSW: PROCEDURE [window: Window.Handle] =
    BEGIN
    boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes];
    boxes.box.dims.h ← 36;
    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: PROCEDURE [why: Event.Reason] =
    BEGIN
    SELECT why FROM
      makeImage, makeCheck => IF running THEN Stopper[];
      startImage, restartCheck, continueCheck => IF running THEN Starter[];
      ENDCASE => NULL;
    END;

  -- initialization

  tool ← Tool.Create[
    name: "Echo Server of January 30, 1981"L, makeSWsProc: MakeSWs,
    clientTransition: ClientTransition, initialState: inactive];
  Event.AddNotifier[@eventItem];
  EchoServerOn[]; -- This may be undesirable

  END.