-- File: BSPSink.mesa,  Last Edit: September 22, 1980  12:56 PM
-- Please don't forget to update the herald.....

DIRECTORY
  Process USING [Yield],
  Stream USING [Handle, GetBlock, Delete],
  String USING [AppendChar, AppendString],
  Time USING [AppendCurrent],

  Event USING [Item, Reason, AddNotifier],
  FormSW USING [
    AllocateItemDescriptor, newLine, ClientItemsProcType, ProcType, FindItem,
    Display, CommandItem, BooleanItem],
  Put USING [Line],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

  StatsDefs USING [
    StatCounterIndex, StatIncr, StatBump, StatsGetCounters, StatsStringToIndex],
  PupStream USING [
    CreatePupByteStreamListener, DestroyPupListener, RejectThisRequest,
    StreamClosing, PupListener, PupAddress],
  PupDefs USING [PupPackageMake, PupPackageDestroy, AppendHostName, veryLongWait],
  PupTypes USING [bspTestSoc];

BSPSink: MONITOR
  IMPORTS
    Process, Stream, String, Time, Event, FormSW, Put, Tool, StatsDefs, PupStream,
    PupDefs =
  BEGIN OPEN StatsDefs, PupDefs;

  herald: STRING = "BSP Sink of September 22, 1980";

  statBytesReceived: PUBLIC StatCounterIndex;
  statConnectionsOpened: PUBLIC StatCounterIndex;
  stats: POINTER TO ARRAY StatCounterIndex OF LONG CARDINAL ← StatsGetCounters[];

  useCount: CARDINAL ← 0;
  pleaseStop, running, verbose, superQuiet: BOOLEAN ← FALSE;
  listener: PupStream.PupListener;

  tool, msg, form: Window.Handle ← NIL;
  eventItem: Event.Item ← [eventMask: 177777B, eventProc: Broom];
  sinks: CARDINAL ← 0;
  maxSinks: CARDINAL ← 4;


  SetupListenerThings: PUBLIC PROCEDURE =
    BEGIN
    statBytesReceived ← StatsStringToIndex["BSP Sink - Bytes received"];
    statConnectionsOpened ← StatsStringToIndex["BSP Sink - Connections opened"];
    END;

  ListenerOn: PUBLIC PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN BEGIN running ← TRUE; Starter[]; END;
    UpdatePicture[];
    END;

  Starter: PROCEDURE =
    BEGIN
    pleaseStop ← FALSE;
    PupDefs.PupPackageMake[];
    listener ← PupStream.CreatePupByteStreamListener[
      PupTypes.bspTestSoc, Sink, veryLongWait, Check];
    END;

  Check: ENTRY PROCEDURE [who: PupStream.PupAddress] =
    BEGIN
    IF sinks >= maxSinks THEN
      PupStream.RejectThisRequest["Sorry, I'm full now."L];
    sinks ← sinks + 1;
    StatsDefs.StatIncr[statConnectionsOpened];
    END;

  ListenerOff: PUBLIC PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN running ← FALSE; Stopper[]; END;
    UpdatePicture[];
    END;

  Stopper: PROCEDURE =
    BEGIN
    pleaseStop ← TRUE;
    UNTIL sinks = 0 DO Process.Yield[]; ENDLOOP;
    PupStream.DestroyPupListener[listener];
    PupDefs.PupPackageDestroy[];
    END;

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

  Sink: PROCEDURE [stream: Stream.Handle, who: PupStream.PupAddress] =
    BEGIN
    KillSinkLocked: ENTRY PROCEDURE = BEGIN sinks ← sinks - 1; END;
    buffer: ARRAY [0..256) OF WORD;
    bytes: CARDINAL;
    Announce[who, "Creating"L];
    BEGIN
    ENABLE PupStream.StreamClosing => CONTINUE;
    UNTIL pleaseStop DO
      [bytes, ] ← Stream.GetBlock[stream, [@buffer, 0, 512]];
      StatsDefs.StatBump[statBytesReceived, bytes];
      ENDLOOP;
    END;
    Stream.Delete[stream];
    Announce[who, "Destroying"L];
    KillSinkLocked[];
    END;

  Announce: PROCEDURE [who: PupStream.PupAddress, arg: STRING] =
    BEGIN
    text: STRING = [100];
    IF superQuiet THEN RETURN;
    Time.AppendCurrent[text];
    String.AppendString[text, "  BSP: "L];
    String.AppendString[text, arg];
    String.AppendString[text, " BSP connection for "L];
    PupDefs.AppendHostName[text, who];
    String.AppendChar[text, '.];
    IF msg # NIL THEN Put.Line[msg, text];
    Put.Line[NIL, text];
    END;

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

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

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    msg ← Tool.MakeMsgSW[window: window, lines: 5];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    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: running];
    items[1] ← FormSW.CommandItem[
      tag: "Stop"L, proc: Stop, place: FormSW.newLine, invisible: ~running];
    items[2] ← FormSW.BooleanItem[
      tag: "Running"L, switch: @running, readOnly: TRUE];
    items[3] ← FormSW.BooleanItem[tag: "Verbose"L, switch: @verbose];
    items[4] ← FormSW.BooleanItem[tag: "SuperQuiet"L, switch: @superQuiet];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN IF new = inactive THEN msg ← form ← NIL; 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

  SetupListenerThings[];
  tool ← Tool.Create[
    name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
  Event.AddNotifier[@eventItem];
  ListenerOn[];
  END.