
#include <netat/appletalk.h>

#include <hash.h>
#include <atalk.h>

#include <rope.h>

#include <xrmonwatch.h>


/*
 * Current zone list
 */

int num_zones = 0;
Rope zones[MaxZones];

/*
 * local pointer to current zone being polled
 */

static int current_zone = -1;

/*
 * local storage of nbp data
 */

static nbpProto nbpr;
static NBPTEntry nbpt[MaxNodes];
static EntityName entity;
static AddrBlock addr;

/*
 * Hash table for nodes
 */

Hash *nodes = (Hash *) NULL;

static void add_node();
static void at_nbpReq();
static void at_nbpRecv();



int 
at_getzones()
{
  int i, cnt;
  OSErr err;
  static int init = 1;
  char *z[MaxZones];

  if(init)
    {
      nodes = sh_create(str_saveString("nodes", strlen("nodes") + 1), 
			NodeHashSize);
      bzero(z, sizeof(z));
      init = 0;
    }

  printf("** getting new zones\n");

  abInit(FALSE);
  if ((err = GetZoneList(z, MaxZones, &cnt)) != noErr) 
    {
      fprintf(stderr, "error %d getting zone list\n", err);
    }
  
  for(i = 0; i < cnt; ++i)
    zones[i] = str_addString(zones[i], str_makeString(z[i]));
  while(i < num_zones)
    {
      zones[i] = str_clearString(zones[i]);
      ++i;
    }
  num_zones = i;

  FreeZoneList(z, cnt);
  return(SUCCESS);
}



void
at_nbpUpdate()
{
  static int init = 0;
  static int last_zoned = 0;
  static int last_polled = 0;
  static int last_looped = 0;
  static int pending = 0;
  unsigned long t;

  if(!init)
    {
      abInit(FALSE);
      checksum_error(FALSE);      
      nbpr.abResult = noErr;
      ++init;
    }

  t = time(0);

  if((current_zone == 0) && ((t - last_looped) < NBPPollInterval))
    return;

  /*
   * we want to refresh the zone list every N. I want the zone update
   * coordinated with the node updates so there's no separate timer here.
   */

  if((t - last_zoned) > ZoneUpdateInterval)
    {
      at_getzones();
      last_zoned = t;
    }


  /*
   * We have a request pending. Collect incoming packets and return
   * until collect interval expires, then process the data and move on.
   */

  if(pending)
    {
      abSleep(0, TRUE);
      if((t - last_polled) < CollectInterval)
	return;

      if(current_zone >= 0)
	at_nbpRecv();
      pending = 0;
    }

  /*
   * move to next zone and make a request.
   */

  if(++current_zone >= num_zones)
    {
      current_zone = -1;
      last_looped = t;
      return;
    }
  
  last_polled = t;
  pending = 1;
  at_nbpReq();

  /*
   * Since we drop packets if we don't check frequently, check 10 times a 
   * second for CollectInterval seconds, then move to next zone.
   */

  return;
}




static void
at_nbpReq()
{
  nbpInit();  
      
  bzero(&entity, sizeof(entity));
  bzero(&nbpr, sizeof(nbpr));
  create_entity(zones[current_zone].s, &entity);
  entity.objStr.s[0]  = '=';
  entity.typeStr.s[0] = '=';

  printf("*** checking zone %s\n", zones[current_zone].s);
  nbpr.nbpRetransmitInfo.retransInterval = 5;
  nbpr.nbpRetransmitInfo.retransCount    = 2;
  nbpr.nbpBufPtr    = nbpt;
  nbpr.nbpBufSize   = sizeof(nbpt);   
  nbpr.nbpDataField = MaxNodes;  
  nbpr.nbpEntityPtr = &entity;

  NBPLookup(&nbpr, TRUE);
  return;
}



 
static void
at_nbpRecv()
{
  int i;

  for (i = 0; i < nbpr.nbpDataField; i++) 
    {
      NBPExtract(nbpt, nbpr.nbpDataField, i, &entity, &addr);
      addr.net = ntohs(addr.net);
      add_node(&entity, &addr);
    }
  printf("got %d\n", nbpr.nbpDataField);
  nbpShutdown();
  return;
}


static void 
add_node(e, addr)
     EntityName *e;
     AddrBlock *addr;
{
  HashEntry *h;
  AtNode *n;
  AtPort *p;
  static char hash[16];
  Rope str;
  Rope data;

  sprintf(hash, "%d.%d", addr->net, addr->node);
  str = str_saveString(hash, strlen(hash) + 1);
  if(!(h = sh_fetch(nodes, str)))
    {
      n = (AtNode *) malloc(sizeof(AtNode));
      bzero(n, sizeof(AtNode));
      n->ports = sh_create(str, PortHashSize);
    }
  else
    n = (AtNode *) h->data.s;

  if(strncasecmp(e->typeStr.s, "Workstation", 11) == 0)
    {
      n->name           = str_addString(n->name, str_makeString(e->objStr.s));
      n->type           = str_addString(n->type, str_makeString(e->typeStr.s));
      
      if(e->zoneStr.s[0] != '*')
	n->zone         = str_addString(n->zone, str_makeString(e->zoneStr.s));
      else
	n->zone         = str_addString(n->zone, zones[current_zone]);
      n->network        = addr->net;
      n->node           = addr->node;
      n->update         = time(0);
    }
  else
    if(!n->name.length)
      n->name = str;    /* put something here */
      
  data.s                = (char *) n;
  data.length           = sizeof(*n);
  data.allocated        = data.length;
  sh_store(nodes, str, data);

  sprintf(hash, "%d", addr->skt);
  str = str_saveString(hash, strlen(hash) + 1);
  if(!(h = sh_fetch(n->ports, str)))
    {
      p = (AtPort *) malloc(sizeof(AtPort));
      bzero(p, sizeof(AtPort));
    }
  else
    p = (AtPort *) h->data.s;

  p->port               = addr->skt;
  p->name               = str_addString(p->name, str_makeString(e->objStr.s));
  p->type               = str_addString(p->type, str_makeString(e->typeStr.s));
  p->zone               = str_addString(p->zone, str_makeString(e->zoneStr.s));
  p->update             = time(0);

  data.s                = (char *) p;
  data.length           = sizeof(*p);
  data.allocated        = data.length;
  sh_store(n->ports, str, data);

  return;
}



  
