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

#include "udptcp.h"


/* Create a packet, probably for pure ack*/
Packet::Packet()
{
	valid=1;
	getHeaderPtr()->len=HEADER_SIZE;
	getHeaderPtr()->seq=0;
	getHeaderPtr()->ackseq=0;	
	getHeaderPtr()->type=0;
}


/*parse an incoming packet */
Packet::Packet(char * newbuf, int len)
{
	valid=0;
	memcpy(buf,newbuf,sizeof(len));
	parse(len);
};

//get a packet from a socket
Packet::Packet(int sock)
{
   struct sockaddr incoming_addr;
   socklen_t incoming_len;   
	
   int rval; 		
   
   
   valid=0;
   memset(buf,0,sizeof buf);   
   incoming_len=sizeof incoming_addr;
   rval=recvfrom(sock,buf,sizeof buf,0,&incoming_addr, &incoming_len);   
   if (rval<=0)
   	printf("ERROR: in reading UDP socket\n");
   else  
   { 
	parse(rval);
   }	
};

//get a packet from a file
Packet::Packet(FILE* infile)
{
	if (infile==NULL) return;
	valid=0;
	if (feof(infile)) return;
	//printf("From file to: %d\n", getDataPtr());
	int len=fread(getDataPtr(),1,S-HEADER_SIZE,infile);
	if (len==0) return;
	printf("%d got from file\n", len);
	struct header * header=getHeaderPtr();
	getHeaderPtr()->len=len+HEADER_SIZE;
	getHeaderPtr()->type=0;
	getHeaderPtr()->type|=TYPE_DATA;
	getHeaderPtr()->seq=0;
	getHeaderPtr()->ackseq=0;
	if (feof(infile))  getHeaderPtr()->type|=TYPE_TERM;
	valid=1;
};


/* print the packet for debug. */
void Packet::print()
{
	printf("length: %d, seq: %d, ackseq:%d TYPE:%d\n",
		getHeaderPtr()->len, getHeaderPtr()->seq,getHeaderPtr()->ackseq, getHeaderPtr()->type);
	/*printf("Content:\n");
	for (int i=HEADER_SIZE; i<getHeaderPtr()->len;i++)
	{
		printf("%c",buf[i]);
	}	
	printf("\n");*/
}






//write a packet to a file
void Packet::output(FILE* outfile)
{
	if (valid)
	{
		if (outfile!=NULL)
		{
			int rval=fwrite(getDataPtr(),1,getDataLen(),outfile);
			if (rval!=getDataLen())
			{
				printf("Incomplete writing to the file %d vs %d\n", rval, getDataLen());
			}
		}
		else
			printf("ERROR: try to write to an unexisting file!");
	}
	else
		printf("ERROR: try to write an invalid packet to the file!\n");
};


//send a packet to the socket
void Packet::send(int sock, struct sockaddr* nextHop, socklen_t nextLen)
{
	if (valid)
	{
				
		int  len=(getHeaderPtr()->len);
		printf("sending size:%d\n",len );
		//sendto(sock,"abc",3,0,nextHop,nextLen);
		
		
		hton();
		int rval=sendto(sock,buf, len,0, nextHop, nextLen);
		ntoh();
		if (rval<0)
		{
			perror("when trying to write to the sock");
		}
		else
		if (rval!=len)
		{
			printf("Incomplete writing to the sock %d vs %d  \n",rval, len);
		}		
	}
	else
		printf("ERROR: try to send an invalid packet!\n");
}

//parse the packet
void Packet::parse(int len)
{
	valid=0;
	if (len<HEADER_SIZE) 
	{
		printf("incomplete packet received %d \n",len);
		return;
	}
	ntoh();
	struct header * header=getHeaderPtr();
	if (len!=header->len)	
	{
		printf("packet len error:%d vs actual:%d\n",header->len, len);
		return;
	}
	printf("Got a packet\n");
	print();
	valid=1;
}

void Packet::ntoh()
{
	getHeaderPtr()->type=ntohs(getHeaderPtr()->type);
	getHeaderPtr()->len=ntohl(getHeaderPtr()->len);
	getHeaderPtr()->seq=ntohl(getHeaderPtr()->seq);
	getHeaderPtr()->ackseq=ntohl(getHeaderPtr()->ackseq);
}
void Packet::hton()
{
	getHeaderPtr()->type=htons(getHeaderPtr()->type);
	getHeaderPtr()->len=htonl(getHeaderPtr()->len);
	getHeaderPtr()->seq=htonl(getHeaderPtr()->seq);
	getHeaderPtr()->ackseq=htonl(getHeaderPtr()->ackseq);
}



SendQueue::SendQueue() //Initialization
{
	una=nxt=tail=0;empty=1;
	for (int i=0;i<Q;i++) queue[i]=NULL;
};

void SendQueue::addPacket(Packet* pkt)
{
	if (!isFull())
	{
		printf("Put a packet in %d mod %d =%d\n",tail, Q, (tail%Q));
		pkt->getHeaderPtr()->seq=tail;
		queue[(tail%Q)]=pkt;
		tail++;
		empty=0;
	}
	else
		printf("ERROR:Trying to add to a full queue\n");
}
void SendQueue::cleanAcked(u_int32_t acked) //clean up the acked packet
{
	u_int32_t diff=acked-nxt;
	if (((int32_t)diff)>0) return; //invalid sequence: after the send_nxt
	
	diff=acked-una;
	if (((int32_t)diff)>=0)
	{
		while (una!=acked)
		{
			delete queue[una%Q];
			queue[una%Q]=NULL;
			una++;
		}
		if (una==tail) empty=1;
	}
	printf("una=%d nxt=%d tail%d\n",una,nxt,tail);
}

void SendQueue::tryToFill(FILE* infile) //Try to read from file and fill the queue
{
	while ((!isFull()) && (infile!=NULL) && (!feof(infile)))
	{
		printf("reading packet from file\n");
		Packet* pkt=new Packet(infile);
		if (pkt->isValid())
		{
			addPacket(pkt);
			printf("packet added to the queue una=%d snd=%d tail=%d\n",una,nxt,tail);
		}
		else
			return;
	};
};


void SendQueue::tryToSend(int sock, struct sockaddr* nextHop, socklen_t nextLen) // try to send the file
{
	u_int32_t diff=nxt-una;
	if (((int32_t)diff)<0) 
	{
		printf("Error: nxt is in front of una: %d %d", nxt, una);
		return; //nxt in front of una? Impossible!
	};
	
	printf("diff=%d una=%d nxt=%d tail=%d\n",diff,una,nxt,tail);
	for (int i=diff;(i<congestionWindow() && (nxt!=tail));i++)
	//You are allowed to send congestionWindow()-diff
	{
		if (nxt!=tail)
		{
			printf("Sending %d in %d\n", nxt,queue[nxt%Q]);
			queue[nxt%Q]->send(sock, nextHop, nextLen);
			nxt++;
		}
	}
	
};


	
int SendQueue::congestionWindow()
{
	return W;
};
	



