-- File: TestTimeServer.mesa 
-- HGM, March 14, 1981  1:45 PM

DIRECTORY
  AddressTranslation USING [AppendNetworkAddress, StringToNetworkAddress],
  BufferDefs USING [BufferAccessHandle, OisBuffer],
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem,
    ItemHandle, newLine,
    ProcType, StringItem],
  InlineDefs USING [BcplLongNumber, BcplToMesaLongNumber],
  MsgSW USING [Post],
  OISCP USING [
    OiscpPackageDestroy, OiscpPackageMake,
    GetFreeSendOisBufferFromPool, ReturnFreeOisBuffer,
    GetOisPacketTextLength, SetOisPacketTextLength],
  OISCPConstants USING [unknownSocketID, timeServerSocket],
  Put USING [Char, CR, Date, Decimal, Line, Text],
  SpecialSystem USING [NetworkAddress],
  Socket USING [
    Abort, AssignNetworkAddress, Create, Delete, GetPacket, PutPacket,
    SetWaitTime, TimeOut, TransferStatus],
  SocketInternal USING [GetBufferPool, SocketHandle],
  Storage USING [Free, FreeStringNil, String],
  String USING [AppendString],
  System USING [],
  Time USING [Packed],
  TimeDefs USING [WestEast],
  Tool USING [
    Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeMsgSW, MakeSWsProc],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle];

TestTimeServer: PROGRAM
  IMPORTS
    FormSW, InlineDefs, MsgSW, Put, String,
    AddressTranslation, OISCP, Socket, SocketInternal,
    Storage, Tool
  EXPORTS Socket, System
  SHARES BufferDefs =
  BEGIN

  -- From TimeServer.mesa
  timeRequest: CARDINAL = 1;
  timeResponse: CARDINAL = 2;

  -- EXPORTED TYPE(S)
  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
  ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle;

  -- global variable declarations
  msg, log, form: Window.Handle ← NIL;
  remoteAddress: STRING ← NIL;
  localAddr: SpecialSystem.NetworkAddress;

  Init: PROCEDURE =
    BEGIN
    [] ← Tool.Create[
      name: "TestTimeServer of March 14, 1981"L, makeSWsProc: MakeThisTool,
      clientTransition: Transition];
    END;

  MakeThisTool: Tool.MakeSWsProc =
    BEGIN
    logFileName: STRING = [40];
    msg ← Tool.MakeMsgSW[window: window, lines: 1];
    form ← Tool.MakeFormSW[window: window, formProc: MakeItemArray];
    Tool.UnusedLogName[logFileName, "TestTimeServer.log$"L];
    log ← Tool.MakeFileSW[window: window, name: logFileName];
    END;

  MakeItemArray: FormSW.ClientItemsProcType =
    BEGIN
    nItems: CARDINAL = 2;
    i: INTEGER ← -1;
    items ← FormSW.AllocateItemDescriptor[nItems];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "Poke"L, place: FormSW.newLine, proc: Poke];
    items[i ← i + 1] ← FormSW.StringItem[
      tag: "RemoteAddress"L, string: @remoteAddress, inHeap: TRUE];
    IF (i + 1) # nItems THEN ERROR;
    RETURN[items, TRUE];
    END;

  Transition: ToolWindow.TransitionProcType =
    BEGIN
    IF old = inactive THEN -- tool is becomming active
      BEGIN
      OISCP.OiscpPackageMake[];
      localAddr ← Socket.AssignNetworkAddress[];
      remoteAddress ← Storage.String[40];
      String.AppendString[remoteAddress,"0#*#"L];
      END
    ELSE
      IF new = inactive THEN
	BEGIN
	remoteAddress ← Storage.FreeStringNil[remoteAddress];
	OISCP.OiscpPackageDestroy[];
	END;
    END;

  Poke: FormSW.ProcType =
    BEGIN
    remoteAddr: SpecialSystem.NetworkAddress;
    cH: ChannelHandle;
    myBufferAccessHandle: BufferDefs.BufferAccessHandle;
    b: BufferDefs.OisBuffer;
    status: Socket.TransferStatus;
    errFlag, hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, ""L];
    remoteAddr ← AddressTranslation.StringToNetworkAddress[remoteAddress !
      ANY =>
	BEGIN
	MsgSW.Post[msg, "RemoteAddress is incorrectly specified; try again."L];
	errFlag ← TRUE;
	CONTINUE;
	END];
    IF errFlag THEN RETURN;
    IF remoteAddr.socket=OISCPConstants.unknownSocketID THEN
      remoteAddr.socket ← OISCPConstants.timeServerSocket;
    cH ← Socket.Create[localAddr];
    myBufferAccessHandle ← SocketInternal.GetBufferPool[cH];
    Socket.SetWaitTime[cH, 1500]; -- milli-seconds

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← OISCP.GetFreeSendOisBufferFromPool[myBufferAccessHandle];
      OISCP.SetOisPacketTextLength[b, 2*3];
      b.ois.transCntlAndPktTp.packetType ← echo;
      b.ois.destination ← remoteAddr;
      b.ois.oisWords[0] ← b.ois.oisWords[1] ← i;
      b.ois.oisWords[2] ← timeRequest;
      Socket.PutPacket[cH, b];
      DO
	b ← Socket.GetPacket[ cH !
	  Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
	status ← LOOPHOLE[b.status];
	SELECT TRUE FROM
	  (status # goodCompletion) =>
	    BEGIN -- some kind of error occurred
	    --PrintSocketEchoError[b, status];
	    Put.Char[log, 'e];
	    END;
	  OISCP.GetOisPacketTextLength[b] # 2*(3 + SIZE[WireTimeFormat])
          OR (b.ois.oisWords[2] # timeResponse) =>
	    BEGIN
	    Put.Char[log, '#];
	    END;
	  ENDCASE =>
	    BEGIN -- the response we were looking for
            wtf: LONG POINTER TO WireTimeFormat = LOOPHOLE[@b.ois.oisWords[3]];
            now: Time.Packed ← LOOPHOLE[InlineDefs.BcplToMesaLongNumber[wtf.time]];
            temp: STRING = [50];
            hit ← TRUE;
            Put.Text[log, "Response from: "L];
            AddressTranslation.AppendNetworkAddress[temp, b.ois.source];
            Put.Line[log, temp];
            Put.Text[log, "Time: "L];
            Put.Date[log, now, full];
            Put.Text[log, ", Zone: "L];
            Put.Text[log, SELECT wtf.zoneS FROM
                west => "W"L, east => "E"L, ENDCASE => ERROR];
            Put.Decimal[log, wtf.zoneH];
            Put.Char[log, ':];
            Put.Decimal[log, wtf.zoneM];
            Put.Text[log, ", DST: "L];
            Put.Decimal[log, wtf.beginDST];
            Put.Text[log, ", "L];
            Put.Decimal[log, wtf.endDST];
	    Put.CR[log];
	    END;
	  OISCP.ReturnFreeOisBuffer[b];
	ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Abort[cH];
    Socket.Delete[cH];
    MsgSW.Post[msg, ""L];
    END;

  WireTimeFormat: TYPE = RECORD [
    time: InlineDefs.BcplLongNumber, -- word 0 and 1
    zoneS: TimeDefs.WestEast, -- start of word 2
    zoneH: [0..177B],
    zoneM: [0..377B], -- end of word 2
    beginDST: WORD, -- word 3
    endDST: WORD, -- word 4
    spare: ARRAY [5..8) OF WORD ← ALL[0]];


  Init[]; -- this gets string out of global frame
  END...