/*
 * receiver - receive network data with error detection & recovery using APB.
 *
 * CSc 645 Programming Assignment #4 - Spring 1995.
 *
 * Written by James Warhol.
 *
 * Compile and execute as described in sender source code.
 *
 */

#include <fcntl.h>			/* defines open flags */
#include <stdio.h>			/* standard file i/o definitions */
#include "defs.h"			/* defines network parameters */
#include "common.h"			/* define common parameters */

/* define chmod modes (which should be defined by include of sys/vnode.h,
   but does not work properly) */

#define VREAD	0400
#define VWRITE	0200

/* define variables */

packet msgbuf;				/* message packet */
packet ack_packet;			/* acknowledgement packet for sender */

int desired_seq_no;			/* seq. number of next packet desired */
int blocks_written;			/* number of data blocks written */
int i;					/* scratch variable */
int ofn_id;				/* output file descriptor */
int st;					/* i/o system request status */

/* define function prototypes */

void initialize(int argc, char *argv[]); /* initialize receiver program */
int receive_packet();			/* wait for incoming data packet */

/* main program - receiver */

void main(int argc, char *argv[])
{

   initialize(argc, argv);		/* initialize receiver program */
   log("receiver: beginning main loop.");

   /* loop indefinitely waiting for packet; when data has been received, 
      write to output file */

   while (receive_packet() == TRUE) {

      /* write valid packet data to output file */

      nnlog("receiver: %d data bytes written to output file; block #%d.", 
         msgbuf.mdr.length, ++blocks_written);
      st = write(ofn_id, &msgbuf.data, msgbuf.mdr.length);
      if (st < 0) error("receiver: unexpected write error in main.");
   }

   log("receiver: end of data transmission from sender detected.");

   /* close the output file and terminate receiver */

   if (close(ofn_id) != 0)
      error("receiver: an error occurred while closing output file.");
   log("receiver: output file closed; terminating receiver.");

   exit(0);

}

/*
 * void initialize(int argc, char *argv[]);
 *
 * initialize initializes processing for the receiver program.
 *
 * Specifically, this means the output file name is fetched from
 * the command line and the file is opened. The desired sequence
 * number, blocks written, and acknowledgement packet ack_packet
 * are also initialized. 
 *
 */

void initialize(int argc, char *argv[])
{

   /* make sure output file name is specified on command line */

   if (argc != 2) error("receiver: you must specify an output file name.");

   /* open output file and check return status */

   slog("receiver: opening output file %s.", argv[1]);
   ofn_id = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, VREAD | VWRITE);
   if (ofn_id < 0)
         error("receiver: an unexpected error occurred when "
            "opening the output file."); 

   /* initialize desired sequence number and number of blocks written so far */

   desired_seq_no = 0;
   blocks_written = 0;

   /* initialize acknowledgement packet header and data fields */

   ack_packet.mdr.length = 0;			/* number of data bytes */
   ack_packet.mdr.seq_no = 0;			/* sequence number */
   ack_packet.mdr.initials[0] = 'j';		/* originator's initials */
   ack_packet.mdr.initials[1] = 'r';
   ack_packet.mdr.initials[2] = 'w';
   for (i = 0; i < DATASIZE; i++) ack_packet.data[i] = ' '; /* clear data */

   return;

}

/*
 * int receive_packet();
 *
 * receive_packet() reads a message packet from standard input that has
 * been sent over the "network" by the sender. The packet is validated 
 * to make sure the length, checksum and initials are as expected. If
 * the length is incorrect, a fatal error is generated. See valid_packet()
 * for details on other error processing.
 *
 * receive_packet() returns TRUE if a packet has been received; it 
 * returns FALSE if there are no more data packets to be read.
 *
 */

int receive_packet()
{

   int wc;				/* word count for read */

   while ((wc = read(0, &msgbuf, sizeof(msgbuf))) > 0) {

      if (wc != sizeof(msgbuf)) error("receiver: packet length is incorrect.");

      if (valid_packet(&msgbuf, "receiver") == TRUE) { /* process if valid */

         /* valid packet received; see if it is the one desired */

         if (msgbuf.mdr.seq_no == desired_seq_no) { /* if right one received */
            nlog("receiver: desired data packet received (seq_no=%d).", 
               msgbuf.mdr.seq_no);

            desired_seq_no = (desired_seq_no + 1) % 2; /* increment seq. no. */
            ack_packet.mdr.ack_no = (short) desired_seq_no;

            /* calculate checksum and send ACK */

            ack_packet.mdr.checksum = 0;       	/* clear checksum initially */
            ack_packet.mdr.checksum = 		/* calc. checksum */
               (short) in_cksum(&ack_packet,sizeof(ack_packet));
            nlog("receiver: sending ACK packet (ack_no=%d).",
               ack_packet.mdr.ack_no);
            st = write(1, &ack_packet, sizeof(ack_packet)); /* send ACK */
            if (st < 0) error("receiver: unexpected error sending ACK.");
            return TRUE;			/* exit status - packet ok */
         }
         else {	   /* sender sent something unexpected - resend last ACK */
            nlog("receiver: duplicate data packet received (seq_no=%d); "
               "ignored.", msgbuf.mdr.seq_no);

            ack_packet.mdr.ack_no = (short) desired_seq_no;
            ack_packet.mdr.checksum = 0;       	/* clear checksum initially */
            ack_packet.mdr.checksum = 		/* calc. checksum */
               (short) in_cksum(&ack_packet,sizeof(ack_packet));
            nlog("receiver: resending ACK packet (ack_no=%d).",
               ack_packet.mdr.ack_no);
            st = write(1, &ack_packet, sizeof(ack_packet)); /* send ACK */
            if (st != sizeof(ack_packet)) 
               error("receiver: unexpected error sending ack.");
         }
      }
   } 

   /* exited while loop - no more data or read error occurred */

   if (wc == 0)					/* no data read */
      return FALSE;				/* exit status - no more data */
   else						/* read error detected */
      error("receiver: unexpected read error in main.");

}

