#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "projcommon.c"
#define BUFLEN 512
#define NPACK 10
#define OPSTRING "p:g:r:q:l:"
#define DEBUG 1 
#define ALOG 1

struct sender_req_options{
	unsigned int port;
	unsigned int requester_port;
	unsigned int rate;
	int sequence_number;
	unsigned int length;
	int over_all_total;
} options;
struct packet * wait_for_request_packet();
void parse_options(int argc, char* const argv[]){
	int opt;
	if(argc != 11){
		error("Usage: sender -p <port> -g <requester port> -r <rate> -q <seq_no> -l <length>\n");
	}

	while((opt = getopt(argc, argv, OPSTRING)) != -1){
		switch(opt){
			case 'p':
				options.port = atoi(optarg);
				if(options.port < 1024 || 
						options.port > 65536){
					error("Invalid entry for port. Must be in range 1024 - 65536\n");
				}
				break;
			
			case 'g':
				options.requester_port = atoi(optarg);
				if(options.requester_port < 1024 || 
						options.requester_port > 65536){
					error("Invalid entry for requester port. Must be in range 1024 - 65536\n");
				}
				break;

			case 'r':
				options.rate = (unsigned int) atoi(optarg);
				if(options.rate < 1 || options.rate > 500)
					error("Non-positive rate. Must be 1-500.\n");
				break;
			case 'q':
				options.sequence_number = atoi(optarg);
				break;
			case 'l':
				options.length = (unsigned int) atoi(optarg);
				break;
			case '?':
				error("Usage: sender -p <port> -g <requester port> -r <rate> -q <seq_no> -l <length>\n");
		}
	}
}

struct packet * wait_for_request_packet(){
	struct packet *request_packet;
	struct sockaddr_in my_addr;
	int sockfd;
	memset((char *) &my_addr, 0, sizeof(my_addr));
	sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
	my_addr.sin_family = AF_INET;
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	my_addr.sin_port = htons(options.port);
	Bind(sockfd, (struct sockaddr*) &my_addr, sizeof(my_addr));
	while(1) {
		if(ALOG) printf("Waiting for request packet\n");
		request_packet = packet_receive(sockfd);
		if(request_packet->type != 'R')
			fprintf(stderr, "Received a packet which is not a request packet.");
		else break;
	}
	return request_packet;
}

void send_file(struct packet* request_packet){
	char filename[18];
	struct packet packet;
	FILE *f;
	int sockfd;
	struct timespec sleep_amount;
	sleep_amount.tv_sec = 0;
	sleep_amount.tv_nsec = (1.0/((double) (options.rate == 1 ? 1.001 : options.rate) ))*1e9;
	/* Make the filename */
	sprintf(filename, "%i.txt", request_packet->sequence);
	if(ALOG) printf("Received request for file: %s\n\n", filename);
	/* open the file for reading */
	f = Fopen(filename, "r");
	packet.length = options.length;
	packet.type = 'D';
	/* Now open a new socket */ 
	sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
	/*memset((char *) &packet.destination, 0, sizeof(packet.destination));*/

	/* set the destination to be the source of the received packet, but 
	** override the port with the user specified port */
	packet.destination = request_packet->source;
	packet.destination.sin_port = htons(options.requester_port);

	/* Populate the dot_addr field */
	packet.dot_addr = malloc(16);
	if(inet_ntop(AF_INET, &packet.destination.sin_addr, packet.dot_addr, 16) == 0) 
		error("inet_pton() failed\n");
	
	while(packet.length == options.length){
		packet.payload = malloc(options.length * sizeof(uint8_t));
		/* read in data */
		packet.length = fread(packet.payload, sizeof(uint8_t), options.length, f);
		/* update sequence number */
		packet.sequence = options.sequence_number;
		options.sequence_number += packet.length; 
		options.over_all_total += packet.length; 
		/* finally send this packet on the socket */ 
		packet_send(sockfd, &packet);
		if(ALOG) alog_print_packet(&packet, 1);
		free(packet.payload);
		/* delay to achieve desired sending rate*/
		nanosleep(&sleep_amount, NULL);
	}
	fclose(f);
	/* Now we build and send the end packet... */
	packet.type = 'E';
	packet.sequence = options.over_all_total;
	packet.length = 0;
	packet.payload = NULL; 
	packet_send(sockfd, &packet);
}

int main(int argc, char* const argv[]) {
	parse_options(argc, argv);
	options.over_all_total = 0;
	send_file(wait_for_request_packet());
	return 0;
}

