/* nspar.c,v 1.1.1.1 1995/02/27 07:38:44 explorer Exp */

/*
 *  Parallel processing utility routines for Netshade
 */

#include "libcommon/common.h"

/*
 * This whole file is one big conditional.
 */

#ifdef NSPARALLEL
#include "nspar.h"
#include "viewing.h"

/*
 * The list of servers
 */
NSServer *Servers;

/*
 * A list of buffers which are totally empty
 */
NSWorkBuffer *WorkBufferFreeList;

/*
 * A list of buffers which are needing to be written to the output file
 */
NSWorkBuffer *WorkBufferWriteList;

/*
 * A list of buffers which are partially rendered
 */
NSWorkBuffer *WorkBufferPendingList;
 

int
tcp_open(port, mode)
     int *port;
     int mode;
{
  struct sockaddr_in sinme;
  int fd;

  if (mode == MODE_SERVER) {
    fprintf(Stats.fstats, "Mode server, port %d\n", *port);
  }

  if (mode == MODE_CLIENT) {
    fprintf(Stats.fstats, "Mode client\n");
  }

  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return (-1);
  }
  
  sinme.sin_family = AF_INET;
  sinme.sin_addr.s_addr = INADDR_ANY;
  if (*port != -1)
    sinme.sin_port = ntohs(*port);
  else
    sinme.sin_port = 0;

  if (bind(fd, (struct sockaddr *)&sinme, sizeof(sinme)) < 0) {
    perror("bind");
    return (-1);
  }

  *port = ntohs(sinme.sin_port);

  fprintf(Stats.fstats, "Opened port %d\n", *port);

  return (fd);
}

int
get_work(fd, frame, start, end)
     int fd;
     int *frame, *start, *end;
{
  NetBuf nb;

  /*
   * If we receive an error here, return that there is no more work to
   * do.  This could be accurate, or it could be that we received junk.
   */
  if (NetReceive(fd, &nb) < 0) {
    RLerror(RL_WARN, "Receive error:  %s\n", strerror(errno));
    return (-1);
  }
  
  if (nb.data.op == OP_TERMINATE) {
    RLerror(RL_WARN, "Our master is done with us, exiting.\n");
    return (-1);
  }
  
  if (nb.render.op != OP_RENDER)
    RLerror(RL_PANIC, "received opcode %d, wanted OP_RENDER\n",
	    nb.render.op);

  *frame = ntohl(nb.render.frame);
  *start = ntohl(nb.render.start);
  *end = ntohl(nb.render.end);
  
  if ((*frame < Options.startframe) || (*frame > Options.endframe) ||
      (*start < 0) || (*end >= Screen.yres)) {
    RLerror(RL_WARN, "Illegal window or frame request\n");
    RLerror(RL_WARN, "frame = %d, start = %d, end = %d\n",
	    *frame, *start, *end);
    return (-1);
  }
  
  return 0;
}

static void
AddServer(newserv)
     NSServer *newserv;
{
  NSServer *srv;

  if (Servers == NULL) {
    Servers = newserv;
    Servers->next = NULL;
    return;
  }
  
  /*
   * search for the end of the server list
   */
  srv = Servers;
  while(srv->next != NULL)
    srv = srv->next;

  srv->next = newserv;
  srv = srv->next;
  srv->next = NULL;
}

int
init_servers()
{
  int count = 0;
  FILE *f;
  char buf[128];
  char tmpnam[128];
  NSServer *newserv = NULL;
  
  f = fopen(Options.hostfile, "r");
  
  if (f == NULL)
    return 0;
  
  /*
   * read a line from the server file, parse it, and try to contact that
   * server.  If we can talk to it, add it to the list of possible servers.
   * if not, drop it and try again.
   */
  for (;;) {
    if (newserv == NULL)
      newserv = (NSServer *)Malloc(sizeof(NSServer));
    
    if (fgets(buf, sizeof(buf) - 1, f) == NULL) {
      fclose(f);
      return (count);
    }
    
    sscanf(buf, "%s %d", tmpnam, &(newserv->port));
    
    newserv->name = strsave(tmpnam);
    
    fprintf(stderr, "Opening %s port %d\n", newserv->name, newserv->port);
    
    newserv->fd = open_server(newserv->name, newserv->port);
    
    if (newserv->fd < 0)
      RLerror(RL_WARN, "Could not contact host %s: %s\n", 
	      newserv->name, strerror(errno));
    else {
      AddServer(newserv);
      newserv = NULL;
      count++;
    }
  }
  
  return (count); /* number of servers we managed to contact */
}

int
open_server(name, port)
     char *name;
     int port;
{
  struct sockaddr_in sin;
  struct hostent *hp;
  int fd;
  
  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return (-1);
  }
  
  sin.sin_family = AF_INET;
  
  sin.sin_port = htons(port);
  
  hp = gethostbyname(name);
  if (hp == NULL) {
    RLerror(RL_WARN, "Could not get address for host %s\n", name);
    return (-1);
  }
  
  if (hp->h_addrtype != AF_INET) {
    RLerror(RL_WARN, "Host %s address is not IP\n", name);
    return (-1);
  }
  
  /* just use the first one returned */
  bcopy(hp->h_addr_list[0], &(sin.sin_addr), hp->h_length);
  
  if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    RLerror(RL_WARN, "Could not connect to host %s port %d\n", name, port);
    return (-1);
  }
  
  return(fd);
}

/*
 * a scratch NetBuf for use in the various functions below
 */
static NetBuf snb;

/*
 * Generic NetBuf send function.
 */
int
NetSend(fd, nb)
     int fd;
     NetBuf *nb;
{
  int cc;
  int len;

  len = nb->data.len + 2;

  /*
   * byte swap the length and opcode fields.  Hopefully, whoever calls this
   * has already swapped whatever fields they are using...
   */
  nb->data.len = htons(nb->data.len);
  nb->data.op  = htons(nb->data.op);

  cc = net_write(fd, nb, len);

  return (cc);
}

/*
 * Send a Version command
 */
int
NetSendVersion(fd, vers)
     int fd;
     uint32 vers;
{
  snb.version.len = sizeof(snb.version);
  snb.version.op  = OP_VERSION;
  snb.version.version = htonl(vers);

  return (NetSend(fd, &snb));
}

/*
 * Send a Terminate command
 */
int
NetSendTerminate(fd)
     int fd;
{
  snb.version.len = sizeof(snb.data);
  snb.version.op  = OP_TERMINATE;
  
  return (NetSend(fd, &snb));
}

/*
 * Send a Render command
 */
int
NetSendRender(fd, frame, start, end)
     int fd;
     uint32 frame;
     uint32 start;
     uint32 end;
{
  snb.render.len   = sizeof(snb.render);
  snb.render.op    = OP_RENDER;
  snb.render.frame = htonl(frame);
  snb.render.start = htonl(start);
  snb.render.end   = htonl(end);

  return (NetSend(fd, &snb));
}

/*
 * Send an Options block (this could be large...
 */
int
NetSendOptions(fd, options)
     int fd;
     RSOptions *options;
{
  return -1;
}

/*
 * Send Picture Start
 */
int
NetSendPictureStart(fd)
     int fd;
{
  snb.data.len   = sizeof(snb.data);
  snb.data.op    = OP_PIC_START;

  return (NetSend(fd, &snb));
}

/*
 * Send Picture End
 */
int
NetSendPictureEnd(fd)
     int fd;
{
  snb.data.len   = sizeof(snb.data);
  snb.data.op    = OP_PIC_END;

  return (NetSend(fd, &snb));
}

/*
 * Send a picture line.  This is not the whole send code for sending lines,
 * this is only enough code to send the SIZES of rgb, alpha, and zbuffer.
 *
 * There will be a total of r+g+b+a+z bytes following this packet, sent in
 * raw form.
 */

int
NetSendPictureLine(fd, bp)
     int fd;
     NSBuffer *bp;
{
  int cc;
  
  snb.pic_line.len   = sizeof(snb.pic_line);
  snb.pic_line.op    = OP_PIC_LINE;
  snb.pic_line.r     = htonl(bp->rlen);
  snb.pic_line.g     = htonl(bp->glen);
  snb.pic_line.b     = htonl(bp->blen);
  snb.pic_line.a     = htonl(bp->alen);
  snb.pic_line.z     = htonl(bp->zlen);

  if ((cc = NetSend(fd, &snb)) < 0)
    return (cc);
  
  if (bp->rlen) {
    if ((cc = net_write(fd, bp->r, bp->rlen)) < bp->rlen)
      RLerror(RL_PANIC, "%s while writing red\n", strerror(errno));
  }
  
  if (bp->glen) {
    if ((cc = net_write(fd, bp->g, bp->glen)) < bp->glen)
      RLerror(RL_PANIC, "%s while writing green\n", strerror(errno));
  }
  
  if (bp->blen) {
    if ((cc = net_write(fd, bp->b, bp->blen)) < bp->blen)
      RLerror(RL_PANIC, "%s while writing blue\n", strerror(errno));
  }
  
  if (bp->alen) {
    if ((cc = net_write(fd, bp->a, bp->alen)) < bp->alen)
      RLerror(RL_PANIC, "%s while writing alpha\n", strerror(errno));
  }
  
  if (bp->zlen) {
    if ((cc = net_write(fd, bp->z, bp->zlen)) < bp->zlen)
      RLerror(RL_PANIC, "%s while writing zbuffer\n", strerror(errno));  
  }
  return (1);
}

int
NetReceivePictureLine(fd, nb, bp)
     int fd;
     NetBuf *nb;
     NSBuffer *bp;
{
  int cc;
  
  if (nb->pic_line.op != OP_PIC_LINE)
    RLerror(RL_PANIC, "Incorrect opcode received!\n");
  
  bp->rlen = ntohl(nb->pic_line.r);
  bp->glen = ntohl(nb->pic_line.g);
  bp->blen = ntohl(nb->pic_line.b);
  bp->alen = ntohl(nb->pic_line.a);
  bp->zlen = ntohl(nb->pic_line.z);
  
  if (bp->rlen) {
    if (bp->r == NULL)
      bp->r = Malloc(bp->rlen);
    cc = net_read(fd, bp->r, bp->rlen);
    if (cc < bp->rlen)
      RLerror(RL_PANIC, "%s while reading red\n", strerror(errno));
  }
  
  if (bp->glen) {
    if (bp->g == NULL)
      bp->g = Malloc(bp->glen);
    cc = net_read(fd, bp->g, bp->glen);
    if (cc < bp->glen)
      RLerror(RL_PANIC, "%s while reading green\n", strerror(errno));
  }
  
  if (bp->blen) {
    if (bp->b == NULL)
      bp->b = Malloc(bp->blen);
    cc = net_read(fd, bp->b, bp->blen);
    if (cc < bp->blen) 
      RLerror(RL_PANIC, "%s while reading blue\n", strerror(errno));
  }
  
  if (bp->alen) {
    if (bp->a == NULL)
      bp->a = Malloc(bp->alen);
    cc = net_read(fd, bp->a, bp->alen);
    if (cc < bp->alen)
      RLerror(RL_PANIC, "%s while reading alpha\n", strerror(errno));
  }
  
  if (bp->zlen) {
    if (bp->z == NULL)
      bp->z = Malloc(bp->zlen);
    cc = net_read(fd, bp->z, bp->zlen);
    if (cc < bp->zlen)
      RLerror(RL_PANIC, "%s while reading zbuffer\n", strerror(errno));
  }
  
  return(1);
}

/*
 * Generic NetBuf receive function.
 */
int
NetReceive(fd, nb)
     int fd;
     NetBuf *nb;
{
  int cc;
  uint16 len;

  /*
   * Zero these out, in case something odd happens.
   */
  len = 0;
  nb->data.op = 0;

  /*
   * read two bytes of data from the connection.  This will tell us how much
   * data follows.
   */
  if ((cc = net_read(fd, &len, 2)) <= 0)
    return (cc);
  
  if (cc != 2)
    RLerror(RL_PANIC, "Short read when reading NetBuf size\n");
  
  nb->data.len = ntohs(len);
  
  if (nb->data.len > sizeof(NetBuf) - 2)
    RLerror(RL_PANIC, "Netbuf size of %d is too large for reality\n",
	    nb->data.len);
  
  /*
   * Now read the remainder of the packet.
   */
  cc = net_read(fd, nb->n_whole + 2, nb->data.len);
  
  /*
   * If we get an error, return it.
   */
  if (cc <= 0)
    return (cc);
  
  /*
   * otherwise, byte swap the opcode field (the length has already been
   * done above) and return that we read something.
   */
  nb->data.op = ntohs(nb->data.op);
  
  return(1);
}

NSBuffer *
GetBuffer()
{
  NSBuffer *bp;
  
  bp = Malloc(sizeof(NSBuffer));
  memset(bp, 0, sizeof(NSBuffer));
  return (bp);
}

NSWorkBuffer *
GetFreeWorkBuffer()
{
  NSWorkBuffer *wbp;
  int i;
  
  if (WorkBufferFreeList == NULL) {
    wbp = Malloc(sizeof(NSWorkBuffer));
    wbp->lines = 0;
    wbp->start = 0;
    wbp->frame = 0;
    wbp->next = NULL;
    for (i = 0 ; i < SERVER_BUFFERS ; i++)
      wbp->buf[i] = GetBuffer();
    return (wbp);
  }
  
  wbp = WorkBufferFreeList;
  WorkBufferFreeList = wbp->next;
  wbp->next = NULL;
  return (wbp);
}

/*
 * net_read & net_write
 */
size_t
net_read(fd, buf, nbytes)
     int fd;
     voidstar buf;
     size_t nbytes;
{
  size_t nr = 0;
  size_t ne = nbytes;
  size_t nb;
  
  while (ne > 0) {
    if ((nb = read(fd, (char *)buf + nr, ne)) <= 0) {
      return ((nr > 0) ? nr : nb);
    }
    nr += nb;
    ne -= nb;
  }
  return (nr);
}
 
size_t
net_write(fd, buf, nbytes)
     int fd;
     voidstar buf;
     size_t nbytes;
{
  size_t ns = 0;
  size_t nr = nbytes;
  size_t nb;
  
  while (nr > 0) {
    if ((nb = write(fd, (char *)buf + ns, nr)) <= 0) {
      return ((ns > 0) ? ns : nb);
    }
    ns += nb;
    nr -= nb;
  }
  return (ns);
}

#endif /* NSPARALLEL */
