-- TimeServer.mesa
-- BLyon, January 16, 1981  5:16 PM
-- HGM, February 10, 1981  6:34 PM

DIRECTORY
  BufferDefs USING [BufferAccessHandle, OisBuffer],
  InlineDefs USING [BcplLongNumber, MesaToBcplLongNumber],
  TimeDefs USING [WestEast],
  Clock USING [GetTimeParms, TimeParameters],
  StatsDefs USING [StatCounterIndex, StatIncr, StatsStringToIndex],
  TimeServerDefs USING [],
  Router USING [FindMyHostID, XmitStatus],
  Socket USING [
    Abort, ChannelAborted, ChannelHandle, Create, defaultWaitTime, Delete,
    PutPacket, GetPacket, SetWaitTime, TimeOut, WaitTime],
  SocketInternal USING [GetBufferPool, SocketHandle],
  System USING [GetGreenwichMeanTime],
  OISCP USING [ReturnFreeOisBuffer, SetOisPacketTextLength, unknownNetID],
  OISCPConstants USING [timeServerSocket],
  SpecialSystem USING [NetworkAddress, SocketNumber];

TimeServer: PROGRAM
  IMPORTS
    InlineDefs, Clock, StatsDefs, System, OISCP, Router, Socket, SocketInternal
  EXPORTS TimeServerDefs, System, Socket
  SHARES BufferDefs =
  BEGIN

  -- Krock to make TYPEs work ok
  ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle;
  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;

  -- These should move to OISCPTypes
  timeRequest: CARDINAL = 1;
  timeResponse: CARDINAL = 2;

  cH: Socket.ChannelHandle;
  localAddr: NetworkAddress;
  timeServerFork: PROCESS;
  timerTimeout: Socket.WaitTime = Socket.defaultWaitTime;

  statPilot: PUBLIC StatsDefs.StatCounterIndex;

  CreateServer: PUBLIC PROCEDURE =
    BEGIN
    localAddr ←
      [net: OISCP.unknownNetID, host: Router.FindMyHostID[],
	socket: OISCPConstants.timeServerSocket];
    cH ← Socket.Create[localAddr, 0, 2, 0, FALSE];
    timeServerFork ← FORK TimeServer[];
    END;

  DeleteServer: PUBLIC PROCEDURE =
    BEGIN Socket.Abort[cH]; JOIN timeServerFork; Socket.Delete[cH]; END;

  TimeServer: PROCEDURE =
    BEGIN
    b: BufferDefs.OisBuffer;
    pool: BufferDefs.BufferAccessHandle ← SocketInternal.GetBufferPool[cH];
    Socket.SetWaitTime[cH, timerTimeout];
    DO
      b ← NIL;
      b ← Socket.GetPacket[
	cH ! Socket.TimeOut => RETRY; Socket.ChannelAborted => GOTO GoAway];
      SELECT LOOPHOLE[b.status, Router.XmitStatus] FROM
	goodCompletion =>
	  BEGIN
	  -- ID is in b.oisWords[0..2), type is in word 2
	  IF b.ois.oisWords[2] = timeRequest THEN
	    BEGIN
	    target: LONG POINTER TO WireTimeFormat = LOOPHOLE[@b.ois.oisWords[3]];
	    parms: Clock.TimeParameters ← Clock.GetTimeParms[];
	    target↑ ←
	      [time: InlineDefs.MesaToBcplLongNumber[
	       System.GetGreenwichMeanTime[]],
		zoneS: IF parms.zone > 0 THEN west ELSE east,
		zoneH: ABS[parms.zone], zoneM: parms.minutes,
		beginDST: parms.beginDst, endDST: parms.endDst];
	    b.ois.destination ← b.ois.source;
	    b.ois.oisWords[2] ← timeResponse;
	    OISCP.SetOisPacketTextLength[b, 2*(3 + SIZE[WireTimeFormat])];
	    Socket.PutPacket[cH, b ! Socket.ChannelAborted => CONTINUE];
	    StatsDefs.StatIncr[statPilot];
	    LOOP;
	    END;
	  END;
	ENDCASE;
      OISCP.ReturnFreeOisBuffer[b];
      REPEAT GoAway => NULL;
      ENDLOOP;
    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]];

  -- NB: There is no check for:
  --  1) The OISCP package is turned on
  --  2) Our clock is reasonable
  -- AutoStartup via START traps
  statPilot ← StatsDefs.StatsStringToIndex["Pilot Date Requests"];
  CreateServer[];
  END.