#include        <unistd.h>
#include        <errno.h>
#include 	"sysdef.h"
#include	"inet.h"
#include        "packets.h"

/* local prototypes */
static int cli_recvfrom(int, char*);
void bail_to_phone(char *, int);         /* move me to my own file when */
                                         /* I actually do something! */

/* Returns 1 for successful send */
int send_packet(sockfd, pserv_addr, buffer, buffsiz)
     int		sockfd;         /* socket FD to send to */
     struct sockaddr_in	*pserv_addr;	/* ptr to sockaddr_in structure */
     char               *buffer;        /* the data to send */
     int                buffsiz;        /* how much data to send */
{
  char	sendline[MAXLINE], recvline[MAXLINE + 1];
  int   n;
  int   echoed;             /* Flag is true if packet has been echoed back */
  int   retry_count;        /* How many tries have there been? */
#define MAX_RETRIES 10      /* How many times do we keep trying? */

/* clear any old echoed packets that might be parked on the socket */
/* This is accomplished simply by reading it out until nothing is left */

  n = 1;
  while (n>0) n = cli_recvfrom(sockfd,recvline);

/* copy the data to be sent */

  bcopy(buffer,sendline,buffsiz);

/* Send the buffer */

  echoed = 0;
  retry_count = 0;
  while ((!echoed) && (retry_count < MAX_RETRIES)) {

    if (sendto(sockfd, sendline, buffsiz, 0, (struct sockaddr *) pserv_addr, 
	       sizeof(*pserv_addr)) != buffsiz)
      err_dump("dg_cli: sendto error on socket");

  /*
   * Now read a message from the socket - it should
   * be an echo.
   */

    sleep(1);
    n = cli_recvfrom(sockfd, recvline);
    if (n < 0) {                  /* if there was a problem getting the echo */
      if (errno != EWOULDBLOCK) {

/*	err_dump("dg_cli: recvfrom error"); */
/* If we know the error (other than nothing's there yet), we really should */
/* do something smart.  For now, just dumbly try, try again. */

	fprintf(stderr,"try #%d\n",++retry_count);
      } else {
	fprintf(stderr,"try #%d\n",++retry_count);
      }
    } else {                      /* if no problem, process the packet */
      recvline[n] = 0;	/* null terminate */

/* Check that it's the same thing */
    
      if (bcmp(sendline,recvline,buffsiz)) {
	fprintf(stderr,"Whoa!  Echoback not the same\n");
      } else {
	echoed = 1;
      }
    }
  }
  if (retry_count >= MAX_RETRIES) 
  {
    bail_to_phone(sendline, buffsiz);
    return 0;
  }
  else
  {
    return 1;
  }

}

/*
 * This routine fills an alarm packet with data, and copies that data into
 * the space at outbuffer.  The size of the output buffer is returned.
 */
int pack_alarm(outbuffer,sequence,outpacket)
     char *outbuffer;               /* buffer to be filled */
     int sequence;                  /* sequence # of this packet */
     struct alarm_packet *outpacket;/* the data packet */
{
  int buffsize;                     /* size of the buffer */

/* Fill packet to send */

  outpacket->sequence = sequence;

/* convert to network byte order */

  outpacket->packet_id = htons(outpacket->packet_id);
  outpacket->experiment = htons(outpacket->experiment);
  outpacket->level = htons(outpacket->level);
  outpacket->sequence = htonl(outpacket->sequence);
  outpacket->datetime[0] = htonl(outpacket->datetime[0]);
  outpacket->datetime[1] = htonl(outpacket->datetime[1]);
  outpacket->datetime[2] = htonl(outpacket->datetime[2]);
  outpacket->signif = htonl(outpacket->signif);

/* pack send info into the buffer */

  buffsize = sizeof(*outpacket);
  bcopy(outpacket,outbuffer,buffsize);
  return(buffsize);
}


/*
 * This routine fills a check packet with data, and copies that data into
 * the space at outbuffer.  The size of the output buffer is returned.
 */
int pack_check(outbuffer,sequence,outpacket)
     char *outbuffer;               /* buffer to be filled */
     int sequence;                  /* sequence # of this packet */
     struct check_packet *outpacket;/* the data packet */
{
  int buffsize;                     /* size of the buffer */

/* Fill packet to send */

  outpacket->sequence = sequence;

/* convert to network byte order */

  outpacket->packet_id = htons(outpacket->packet_id);
  outpacket->experiment = htons(outpacket->experiment);
  outpacket->datetime[0] = htonl(outpacket->datetime[0]);
  outpacket->datetime[1] = htonl(outpacket->datetime[1]);
  outpacket->datetime[2] = htonl(outpacket->datetime[2]);
  outpacket->sequence = htonl(outpacket->sequence);

/* pack send info into the output buffer */

  buffsize = sizeof(*outpacket);
  bcopy(outpacket,outbuffer,buffsize);

  return(buffsize);
}

int get_packet(sockfd, pcli_addr, mesg)
     int		sockfd;         /* the socket to listen on */
     struct sockaddr_in	*pcli_addr;	/* ptr to client sockaddr structure */
     char	        *mesg;          /* input buffer */
{
  int	       i, n, clilen;
  static char  lastpacket[NUM_EXP][MAXLINE]; /* array of the last packets received */
  int          this_expt;         /* Where the current packet is from */
  int          new_packet;        /* Is this a different packet than before? */
  static int   first = 1;         /* first call of this routine? */
  u_short      id_array[2];       /* place to unpack ID's into */
  static char  nulldata[2];       /* set to zeros for comparison */


  if (first) {
    first = 0;
    bzero(nulldata,2);
    for (i=0; i<NUM_EXP; i++) 
      bzero(lastpacket[i],MAXLINE);
  }

  new_packet = 0;
  while (!new_packet) {
/* read a buffer from the socket */

    clilen = sizeof(*pcli_addr);
    n = recvfrom(sockfd, mesg, MAXLINE, 0, (struct sockaddr *)pcli_addr, 
		 &clilen);
    if (n < 0)
      err_dump("dg_echo: recvfrom error");
    mesg[n] = 0;           /* null terminate */

/* echo it back out */
    
    if (sendto(sockfd, mesg, n, 0, (struct sockaddr *)pcli_addr, clilen) != n)
      err_dump("dg_echo: sendto error");

/* See if we've already got this one */

    bcopy(mesg,id_array,4);
    this_expt = ntohs(id_array[1]);
    if (!bcmp(lastpacket[this_expt-1],nulldata,2)) {
      new_packet = 1;
      bcopy(mesg,lastpacket[this_expt-1],n);
    } else {
      if (bcmp(mesg,lastpacket[this_expt-1],n)) {
	new_packet = 1;
	bcopy(mesg,lastpacket[this_expt-1],n);
      }
      else
      {
	printf("*** Packet repeat ignored, experiment %d ***\n",this_expt);
      }
    }
  }
  printf("From %s:\n",inet_ntoa(pcli_addr->sin_addr));
  return(n);
}

/* This routine deals with a GC alarm packet */

void handle_alarm(inbuffer, n, inpacket)
     char *inbuffer;              /* the input buffer */
     int n;                       /* the size of that buffer */
     struct alarm_packet *inpacket;/* an alarm packet */
{

/* Check that the packet size is correct, and fill the structure if it is */

  if (n != sizeof(*inpacket)) {
    fprintf(stderr,"Whoa!  %d is wrong packet size for this type (%d)\n",
	    n,sizeof(*inpacket));
    return;
  } else {
    bcopy(inbuffer,inpacket,n);
  }

/* Convert to local byte order */

  inpacket->packet_id = ntohs(inpacket->packet_id);
  inpacket->experiment = ntohs(inpacket->experiment);
  inpacket->level = ntohs(inpacket->level);
  inpacket->sequence = ntohl(inpacket->sequence);
  inpacket->datetime[0] = ntohl(inpacket->datetime[0]);
  inpacket->datetime[1] = ntohl(inpacket->datetime[1]);
  inpacket->datetime[2] = ntohl(inpacket->datetime[2]);
  inpacket->signif = ntohl(inpacket->signif);
}


/* This routine deals with an experiment alive check packet */

void handle_check(inbuffer, n, inpacket)
     char *inbuffer;               /* the input buffer */
     int n;                        /* the size of that buffer */
     struct check_packet *inpacket;/* a check packet */
{

/* Check that the packet size is correct, and fill the structure if it is */
  if (n != sizeof(*inpacket)) {
    fprintf(stderr,"Whoa!  %d is wrong packet size for this type (%d)\n",
	    n,sizeof(*inpacket));
    return;
  } else {
    bcopy(inbuffer,inpacket,n);
  }

/* Convert to local byte order */

  inpacket->packet_id = ntohs(inpacket->packet_id);
  inpacket->experiment = ntohs(inpacket->experiment);
  inpacket->datetime[0] = ntohl(inpacket->datetime[0]);
  inpacket->datetime[1] = ntohl(inpacket->datetime[1]);
  inpacket->datetime[2] = ntohl(inpacket->datetime[2]);
  inpacket->sequence = ntohl(inpacket->sequence);
}


/*
 * This routine does the recvfrom on the clientside.  
 * Messy sgi bull hidden here.
 */
int cli_recvfrom(sockfd, recvline)
     int  sockfd;
     char *recvline;
{
#ifdef sgi
  struct sockaddr_in *return_addr = NULL; /* ptr to appropriate sockaddr structure */
  int returnlen;            /* length of returned buffer */
#endif
  int n;

#ifdef sgi
  returnlen = sizeof(return_addr);
  n = recvfrom(sockfd, recvline, MAXLINE, 0,
	       (struct sockaddr *)return_addr, &returnlen);
#else
  n = recvfrom(sockfd, recvline, MAXLINE, 0,
	       (struct sockaddr *) 0, (int *) 0);
#endif
  return(n);
}


void bail_to_phone(buffer, buffsiz)
     char *buffer;
     int  buffsiz;
{
  /* This routine, which does nothing as yet, is intended
for a backup send via modem if the network send fails. */

  fprintf(stderr,"GC Phone Home!\nNothing to do here yet.\n");
  return;
}
