/* PupMisc.c
  Swinehart, July 15, 1982  2:18 PM, Miscellaneous Network Services
  Currently network time, routing request functions.
  Cosmetic mods, L. Stewart October 20, 1982  9:42 PM
  L. Stewart November 13, 1982  4:38 PM, more Blocking
  L. Stewart November 13, 1982  5:45 PM, general munging
  L. Stewart November 23, 1982  9:54 AM, restore statics
  L. Stewart November 28, 1982  6:36 PM, formatting
  L. Stewart December 27, 1982  2:53 PM
     flush nested declarations
  L. Stewart February 10, 1983  3:49 PM, routing
  L. Stewart February 23, 1983  12:50 PM, remainder -> GetRem
  L. Stewart March 6, 1983  4:28 PM, remove incl Alloc.h
  July 11, 1983  9:25 AM by Stewart, Route changed
  July 18, 1983  1:13 PM by Stewart, localHost
 */

#include <Ec.h>
#include <Env.h>
#include <Queue.h>
#include <Pup.h>

struct Calendar {
  int time[2];
  int zoneInfo;
  int dstStartDay;
  int dstEndDay;
  int base[2];  /* last ms timer, for updating time */
  };

struct RoutingEntry {
  byte net;      /* Target net */
  byte gateNet;  /* A net along the route */
  byte gateHost; /* A gateway on gateNet */
  byte hops;     /* hops from responding gateway to target net */
  };

/* OS */
extern Move2();
extern Timer();
extern DoubleDifference();
extern DoubleIncrement();
extern DoubleUDiv();
extern GetRem();
extern Marshall();
extern CallSwat();
extern SetTmr();
extern TmrExp();
extern Swab();

/* Queue package */
extern InitQueue();

/* context package */
extern Block();

/* Pup package */
extern GetPBI();
extern ReleasePBI();
extern OpenLevel1Socket();
extern WaitUntilSent();
extern CloseLevel1Socket();
extern AppendStringToPup();
extern SendPup();

/* Various counters */
static int simpleSent;
static int simpleRcvd;
static int calSent;
static int calRcvd;
static int routSent;
static int routRcvd;

/* ----------- Time and Date acquisition and maintenance -------------- */

struct Calendar calendar;

/*----------------------------------------------------------------------- */
ReadCalendar(c)
/*----------------------------------------------------------------------- */
  int c[];
  {
  int t[], newbase[2], ms[2];
  if (calendar.base[0] == 0) Timer(calendar.base);
  t = calendar.time;
  Timer(newbase);
  Move2(ms, newbase);
  DoubleDifference(ms, calendar.base);
  DoubleIncrement(t, DoubleUDiv(ms, 1000));
  ms[1] = 0;
  ms[0] = GetRem();
  DoubleDifference(newbase, ms);
  /* newbase is now newbase0+m*1000 */
  Move2(calendar.base, newbase);
  Move2(c, t);
  };

/*----------------------------------------------------------------------- */
InitCalendar()
/*----------------------------------------------------------------------- */
  {
  struct PBI *pbi;
  struct Pup *p;
  int temp;
  Timer(calendar.base);
  Timer(calendar.time);  /* Default value, if no answer */
  pbi = SimpleExch(0206, 0207, 4, 3);
  calSent += simpleSent;
  if (pbi == 0) return; /* didn't get an answer, fake it */
  calRcvd += 1;
  p = pbi->pup;
  Marshall(true, calendar.time, p->data.words, 5);
  ReleasePBI(pbi);
  temp = calendar.time[0];
  calendar.time[0] = calendar.time[1];
  calendar.time[1] = temp;
  };

/* ----------- Simple network routing table maintenance  -------------- */

extern struct SocketEntry pupSockets[1 /*maxPupSockets*/];
extern struct Route routingTable[maxNetworks];
extern int localNet;
extern int localHost;
int routingSoc;

/*----------------------------------------------------------------------- */
static RoutePr(pbi)
/*----------------------------------------------------------------------- */
  struct PBI *pbi;
  {
  struct Pup *p;
  int numEntries, i;
  struct RoutingEntry entries[];
  int net, hops;
  struct Route *route;
  p = pbi->pup;
  if (p->type == 0201) {
    routRcvd += 1;
    numEntries = (Swab(p->length) - pupOvBytes) >> 2;
    entries = (struct RoutingEntry *) p->data.words;
    for (i = 0; i < numEntries; i += 1) {
      Block();
      net = entries[i].net;
      route = &routingTable[net];
      hops = entries[i].hops + 1;
      if ((route->hops > hops) || (route->host == p->sPort.host) || ((route->hops == hops) && (route->age == 0x00ff))) {
        route->host = p->sPort.host;
        route->hops = hops;
        route->age = 0;
        };
      };
    };
  ReleasePBI(pbi);
  };

/*----------------------------------------------------------------------- */
RequestRoute(net)
/*----------------------------------------------------------------------- */
  int net;
  {
  /* net not used for anything at present. */
  struct PBI *pbi;
  struct SocketEntry *pse;
  struct Port lclPort;
  if (!routingSoc) {
    lclPort.net = localNet;
    lclPort.host = localHost;
    lclPort.socket.LS = 0;
    lclPort.socket.ms = Swab(2);
    routingSoc = OpenLevel1Socket(&lclPort, &RoutePr, 0);
    if (!routingSoc) return;
    };
  pbi = GetPBI();
/*
  pse = &pupSockets[routingSoc];
  Move2(&pbi->pup->sPort.socket, &pse->port.socket);
 */
  pbi->pup->sPort.socket.LS = 0;
  pbi->pup->sPort.socket.ms = Swab(2);
  SimpleSend(pbi, 0200, 02);
  routSent += 1;
  };


/* ----- Simple broadcast services packet exchange implementation  ----- */

static struct PBI *rcvPBI;
static int rcvType;
static int z[2];  /* actually a null ShortSTRING */
static int counter[2];

/*----------------------------------------------------------------------- */
static RcvPr(pbi)
/*----------------------------------------------------------------------- */
  struct PBI *pbi;
  {
  if ((pbi->pup->type != rcvType) || (rcvPBI != 0)) ReleasePBI(pbi);
  else {
    rcvPBI = pbi;
    simpleRcvd += 1;
    };
  };

/*----------------------------------------------------------------------- */
struct PBI *SimpleExch(sendType, recvType, servSoc, tries)
/*----------------------------------------------------------------------- */
  /* Broadcasts packet of type sendType to servSoc, expects
     packet of type recvType in response.  Tries tries times.
     Returns 0 if no joy, else recieved PBI.
   */
  int sendType, recvType, servSoc, tries;
  {
  struct PBI *pbi;
  struct Pup *p;
  int soc, i;
  struct Queue tQ[1];
  int tmr[1];
  int wait;
  InitQueue(tQ)
  pbi = GetPBI();
  p = pbi->pup;
  Timer(&p->sPort.socket.LS);
  rcvPBI = 0;
  if (!(soc = OpenLevel1Socket(&p->sPort, &RcvPr, 0))) CallSwat(ecPup1+10);
  pbi->queue = tQ;
  rcvType = recvType;
  Timer(counter);
  wait = 100;
  for (i = 0; ((i<tries) && (rcvPBI == 0)); i += 1) {
    simpleSent += 1;
    SimpleSend(pbi, sendType, servSoc);
    WaitUntilSent(pbi);  /* Removed from tQ after sent */
    SetTmr(wait, tmr);
    /* Give answer time to arrive  */
    while ((!TmrExp(tmr)) && (rcvPBI == 0)) Block();
    wait = wait + 500;
    };
  ReleasePBI(pbi);
  CloseLevel1Socket(soc);  /* We either got it or we didn't! */
  return(rcvPBI);
  };

/*----------------------------------------------------------------------- */
SimpleSend(pbi, sendType, servSoc)
/*----------------------------------------------------------------------- */
  /* Broadcasts packet, assuming  source port socket field is already set up,
    and socket is Open to recieve */
  struct PBI *pbi;
  int sendType, servSoc;
  {
  struct Pup *p;
  p = pbi->pup;
  p->dPort.net = localNet;
  /* Host is 0 . . . broadcast (zero'ed by GetPBI) */
  p->dPort.socket.ms = Swab(servSoc);
  p->sPort.net = localNet;
  p->sPort.host = localHost;
  p->type = sendType;
  if (!counter[0]) Timer(counter);
  DoubleInc(counter, 1);
  p->id[0] = counter[1];
  p->id[1] = counter[0];
  Zero(z, 2);
  AppendStringToPup(pbi, 0, z);  /* Just to compute length of empty packet */
  SendPup(pbi);
  };