/************************************************************/
/*                                                          */
/*  The header file of packet definition                    */
/*  For CS 145 (Fall 2002) Lab 4                            */
/*  Author: Xiaoliang (David) Wei                           */
/*  Date:   Dec 05, 2002                                    */
/*                                                          */
/************************************************************/


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>

/* 
The maximum packet size is S=1460.
*/
#define S 500

/*
Type Field:
1: Starting
2: Terminating
4: With DATA
8: With ACK
16:RETRANSMITTED
32: LOSS packet
64: TERM2 packet
*/
#define TYPE_START 1
#define TYPE_TERM 2
#define TYPE_DATA 4
#define TYPE_ACK 8
#define TYPE_RETRX 16
#define TYPE_LOSS 32
#define TYPE_TERM2 64

/*
Packet Format:
0-1 Bytes: type
2-5 Bytes: Length
6-9 Bytes: Sequence Number (per packet)
10-13 Bytes: Ack Sequence Number
14- Bytes: Data
*/
typedef struct header
{
	u_int16_t type;
	u_int32_t len;
	u_int32_t seq;
	u_int32_t ackseq;
};

#define packet_header(x) (struct header *)x
//Translate a pointer to a packet to a pointer to the header of a packet.

#define HEADER_SIZE (sizeof (struct header))


class Packet 
{
	public:
	int valid;
	char buf[S+1];
	
	public:
	Packet();			//Create an empty packet, possibly for pure ack
	Packet(char* newbuf, int len); //parse the buf and translate into packet format
	Packet(int sock); //read from socket and create the packet
	Packet(FILE* infile); //read from file and create the packet
	struct header * getHeaderPtr() {return (struct header*)buf;}
	char* getDataPtr() {char* tmp=buf; tmp+=HEADER_SIZE; return tmp;};
	int getDataLen() {return getHeaderPtr()->len-HEADER_SIZE;}
	int isValid() {return valid; } 
	void print(); //print the packet
	void output(FILE* outfile); //save the packet to a file
	void send(int sock, struct sockaddr* nextHop, socklen_t nextLen); //send a packet to the socket
private:
	void setType(u_int16_t newtype){ getHeaderPtr()->type=newtype;};
	void parse(int len); //parse a packet
	void hton();
	void ntoh();
};


/* static congestion window, equal to 10 */
#define W (u_int32_t) 10


/* queue size, equal to ten times of window */
#define Q W*10

/* This is the queue for packets sent but not acked, and packets to be sent
 * It's a cycling queue with an empty flag 
 */
class SendQueue
{
	Packet* queue[Q];
	u_int32_t una; //This is the pointer for the first packet that is not acked
	u_int32_t nxt; //This is the pointer for the first packet that is not sent (for the 1st time)
	u_int32_t tail; //The tail of the queue: pointing at the first free space in the queue	
	int empty; //flag empty. when (tail==una)&& empty, the queue is empty, if (((tail%Q)==)una%Q)) && (!empty)) the queue is full.
	
	public:
	SendQueue(); //Initialization
	void tryToFill(FILE* infile); //Try to read from file and fill the queue
	void tryToSend(int sock, struct sockaddr* nextHop, socklen_t nextLen); // try to send the file
	void cleanAcked(u_int32_t ackseq); //clean up the acked packet
	
	void addPacket(Packet* pkt);
	int packet_in_flight() { if (empty) return 0; else return (nxt+Q-una)%Q; }; //Calculate how many packets are sent but not acked
	int packet_to_send() {if (empty) return 0; else return (tail+Q-nxt)%Q; }; //Calculate how many packets are going to be sent
	int isFull() {if (empty) return 0; else return ((tail%Q)==(una%Q)); };
	int isEmpty() {return empty;};
	int congestionWindow();
	void retransmit() {nxt=una;}
};


/*Time out value in micro second*/
#define RTO 300*1000
#define TERM RTO*20

