-- File: BSPSink.mesa - last edit:
-- AOF                 17-Feb-88 17:06:28
-- WIrish               5-Feb-88 12:03:17
-- HGM                 25-Jun-85  3:14:02
-- Copyright (C) 1983, 1984, 1985, 1988 by Xerox Corporation. All rights reserved. 
DIRECTORY
  Environment USING [Byte],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  FormSW USING [
    AllocateItemDescriptor, newLine, ClientItemsProcType, ProcType, FindItem,
    Display, CommandItem, BooleanItem],
  Process USING [Yield],
  Put USING [Line],
  Runtime USING [GetBcdTime],
  Stream USING [Handle, GetBlock, Delete],
  String USING [AppendChar, AppendString],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Time USING [Append, AppendCurrent, Unpack],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

  PupDefs USING [PupPackageMake, PupPackageDestroy, AppendHostName, veryLongWait],
  PupStream USING [
    CreatePupByteStreamListener, DestroyPupListener, RejectThisRequest,
    StreamClosing, PupListener, PupAddress],
  PupTypes USING [bspTestSoc],
  Stats USING [
    StatCounterIndex, StatIncr, StatBump, StatsStringToIndex],
  StatsOps USING [StatArray, statGrand];

BSPSink: MONITOR
  IMPORTS
    Event, FormSW, Process, Put, Runtime, Stream, String, Supervisor, Time, Tool,
    PupDefs, PupStream, Stats, StatsOps =
  BEGIN OPEN Stats, PupDefs;
  
  statBytesReceived: PUBLIC StatCounterIndex;
  statConnectionsOpened: PUBLIC StatCounterIndex;
  stats: StatsOps.StatArray ← StatsOps.statGrand;

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

  tool, msg, form: Window.Handle ← NIL;
  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];
  sinks: CARDINAL ← 0;
  maxSinks: CARDINAL ← 4;

  Init: PROCEDURE =
    BEGIN
    herald: LONG STRING = [100];
    String.AppendString[herald, "BSP Sink Tool of "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    SetupListenerThings[];
    tool ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  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
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      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;
    Stats.StatIncr[statConnectionsOpened];
    END;

  ListenerOff: 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;
    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;
    bytesPerClump: CARDINAL = 512;
    buffer: PACKED ARRAY [0..bytesPerClump) OF Environment.Byte;
    bytes: CARDINAL;
    Announce[who, "Creating"L];
    BEGIN
    ENABLE PupStream.StreamClosing => CONTINUE;
    UNTIL pleaseStop DO
      [bytes, ] ← Stream.GetBlock[stream, [@buffer, 0, bytesPerClump]];
      Stats.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: Supervisor.AgentProcedure =
    BEGIN
    SELECT event FROM
      EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume =>
        IF running THEN Stopper[];
      ENDCASE => NULL;
    END;

  -- initialization
  Init[];
  ListenerOn[];
  END.