/*
 * CSc 645 Programming Assignment #4 - Spring 1995.
 *
 * sender - send network data with error detection & recovery using 
 * the Alternating Bit Protocol.
 *
 * This is part 1 of the implementation, which uses a simple busy 
 * waiting "timeout" mechanism.
 *
 * Written by James Warhol.
 *
 * Compile sender with:   cc -o sender   sender.c   chksum.c common.c
 *
 * Compile receiver with: cc -o receiver receiver.c chksum.c common.c
 *
 * Note that the routines from /Users/murphy/645/hw4/error.c have been
 * slightly modified and included in common.c.
 *
 * Execute with: execute sender receiver net    infile outfile
 *                  or
 *               execute sender receiver noerrs infile outfile
 *
 */

#include <errno.h>			/* defines file open error codes */
#include <fcntl.h>			/* defines open flags */
#include <stdio.h>			/* standard file i/o definitions */
#include "defs.h"			/* defines network parameters */
#include "common.h"			/* defines common parameters */

/* define variables */

packet msgbuf;				/* message packet */

int sender_seq_no;			/* sequence number of current packet */
int blocks_read;			/* number of data blocks read so far */

int errno;				/* error number for i/o errors */
int i, j;				/* scratch variables */
int ifn_id;				/* input file descriptor */
int st;					/* i/o system request return status */
int wc;					/* word count for read */

/* define function prototypes */

void initialize(int argc, char *argv[]); /* initialize sender program */
int send_packet();			/* send packet across network */

/* main program - sender */

void main(int argc, char *argv[])
{

   initialize(argc, argv);		/* initialize sender program */

   /* loop repeatedly reading DATASIZE bytes from the input file into
      the packet buffer; send packet across network */

   log("sender: beginning main loop."); 

   while ((wc=read(ifn_id, msgbuf.data, DATASIZE)) > 0) {
      nnlog("sender: %d data bytes read from input file; block #%d.", 
         wc, ++blocks_read);

      /* set data byte count in packet header and pad data bytes with spaces */

      msgbuf.mdr.length = (short) wc;	/* number of data bytes */
      for (i = wc; i < DATASIZE; i++) msgbuf.data[i] = ' ';

      if (send_packet() != TRUE)	/* if unable to send packet */
         error("sender: an uncorrectable data transmission error occurred.");
   }

   if (wc < 0) error("sender: unexpected read error in main.");
   log("sender: end of file encountered on input file.");

   /* close the input file and terminate sender */

   if (close(ifn_id) != 0) 
      error("sender: an error occurred while closing input file.");
   log("sender: input file closed; terminating sender.");

   exit(0);

}

/*
 * void initialize(int argc, char *argv[]);
 *
 * initialize initializes processing for the sender program.
 * Specifically, this means the input file name is fetched from
 * the command line, the file is opened, and the author's initials
 * (jrw) are placed in the packet header.
 *
 * The values of sender_sequence_no and blocks_read are also initialized.
 *
 */

void initialize(int argc, char *argv[])
{

   /* make sure input file name is specified on command line */

   if (argc != 2) error("sender: you must specify an input file name.");

   /* open input file and check return status */

   slog("sender: opening input file %s.", argv[1]);
   ifn_id = open(argv[1], O_RDONLY, 0);
   if (ifn_id < 0) {
      if (errno == ENOENT) 
         error("sender: the input file does not exist.");
      else
         error("sender: an unexpected error occurred when opening "
            "the input file."); 
   }

   /* set initials in packet header */

   msgbuf.mdr.initials[0] = 'j';
   msgbuf.mdr.initials[1] = 'r';
   msgbuf.mdr.initials[2] = 'w';

   /* initialize sender sequence number and count of data blocks read */

   sender_seq_no = 0;
   blocks_read = 0;

   return;

}

/*
 * int send_packet();
 *
 * send_packet sends the current packet across the network after setting
 * the sequence number and calculating the Internet checksum.
 *
 * send_packet returns a value of TRUE if the packet was sent and 
 * received without errors. send_packet returns a value of FALSE if
 * no ackwledgement was ever received for the packet.
 *
 */

int send_packet()
{

   int readloop, tryloop;		/* loop counters */

   packet ack_packet;			/* acknowledgement packet */

   msgbuf.mdr.seq_no = (short) sender_seq_no;	/* set seq. no. in header */

   /* calculate checksum and place in outgoing packet header */

   msgbuf.mdr.checksum = 0;       /* clear checksum initially */
   msgbuf.mdr.checksum = (short) in_cksum(&msgbuf,sizeof(msgbuf));

   /* attempt to send this packet over the network up to NUMTRIES 
      times, until acknowledgement received or until send times out */

   for (tryloop = 0; tryloop < NUMTRIES; tryloop++) {

      /* write packet to network (via standard output) */

      if (tryloop == 0)
         nlog("sender: sending data packet (seq_no=%d).", msgbuf.mdr.seq_no);
      else
         nlog("sender: send timed out; resending data packet (seq_no=%d).", 
            msgbuf.mdr.seq_no);
      st = write(1, &msgbuf, BUFSIZE);
      if (st < 0) error("sender: unexpected write error in send_packet.");

      /* busy wait: loop up to NUMREADS times, each time checking for an 
         ACK from the receiver */

      for (readloop = 0; readloop < NUMREADS; readloop++) {
         sleep(1);	/* give receiver a chance to respond */
         st = read(0, &ack_packet, sizeof(ack_packet));
         if (st < 0 && errno != EWOULDBLOCK) /* ignore EWOULDBLOCK errors */
            nnlog("sender: unexpected network read error: st=%d, errno=%d.", 
            st, errno);
         if (st > 0) {	/* something has been received across network */
            if (st != sizeof(ack_packet))
               error("sender: ACK packet length is incorrect.");

            /* validate that checksum and initials are correct */

            if (valid_packet(&ack_packet, "sender") == TRUE) {

               /* checksum and initials are correct - inspect packet */

               if (sender_seq_no != ack_packet.mdr.ack_no) { /* send worked */
                  nnlog("sender: valid ACK (ack_no=%d) rcv'd; data packet "
                     "(seq_no=%d) arrived ok.", 
                     ack_packet.mdr.ack_no, sender_seq_no);
                  sender_seq_no = ack_packet.mdr.ack_no; /* update seq. no. */
                  return TRUE;		/* packet sent successfully */
               }
               else { /* receiver still waiting for this packet - resend */
                  nlog("sender: valid ACK rcv'd; indicates packet lost; "
                     "resending (seq_no=%d).", sender_seq_no);
                  st = write(1, &msgbuf, BUFSIZE);
                  if (st < 0) 
                     error("sender: unexpected write error in send_packet.");
               }
            }
         }
      }
   }

   /* no correct data has been received; send has completely failed */

   nlog("sender: all attempts to send packet have failed (seq_no=%d).", 
      msgbuf.mdr.seq_no);
   return FALSE;			/* packet not sent properly */

}
