/*
 * Escrito por : Giovani Facchini
 * mailto: giovani@exatas.unisinos.br
 *
 * Receptor que utiliza UDP
 * Desenvolvido para sistema operacional linux
*/


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>

#define QTD_DADOS 1492
#define GRAFIC_INTERVAL 1000000
//#define BLA

/* GRAFIC_INTERVAL indica o intervalo dos pontos no gráfico em microsegundos */
/* Descomente a definição de DEBUGING para ver o que o receptor está fazendo*/

int main(int argc, char **argv){
	struct  sockaddr_in peer;
	int  s;//socket
	int porta, rc, i, peerlen; //rc = quantidade recebida de dados
	char path[256];//caminho do arquivo destino
	int fd;//descritor do arquivo destino
       	FILE *grafico;//descritor do arquivo de gráfico
	struct buf {
		char buffer[QTD_DADOS];
		unsigned long long seq;
                int tam;
	}buffer;//estrutura do pacote
	unsigned long seq, seq_antiga, pkt_esperado;
	char hand[10];//controle de hand-shake
	struct timeval tv, time_novo, time_antigo;
	long temp;
	float tempo;

	//se faltam argumentos
	if(argc < 5) {
		printf("Falta parametros.\n");
		printf("Parametros validos:\n");
		printf("-p <porta>\n");
		printf("-n <nome_do_arquivo>\n");
		exit(1);
	}

	//pega os argumentos
	for(i=1; i < argc; i++) {
		if(argv[i][0]=='-'){
			switch(argv[i][1]){
				case 'p':
					i++;
					porta = atoi(argv[i]);
					if(porta < 1024) {
						printf("Porta invalida\n");
						exit(1);
					}
					break;
				case 'n':
					i++;
					strcpy(path, argv[i]);
					break;
				default:
					printf("Parametro %d: %s invalido\n",i,&argv[i]);
					exit(1);
				}
		}else{
			printf("Parametro %d: %s invalido\n",i,&argv[i]);
			exit(1);
		}
	}

	// define os parametros de peer
	peer.sin_family = AF_INET;
	peer.sin_port = htons(porta);
	peer.sin_addr.s_addr = htonl(INADDR_ANY); // Recebe de qualquer IP

	//cria socket
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if( s < 0) {
		 printf("Falha na criacao do socket\n");
		 exit(1);
 	}

	//amarra socket
	if(bind(s,(struct sockaddr *) &peer, sizeof(peer))) {
		 printf("Erro no bind\n");
		 exit(1);
	}

	//abre arquivo destino
	if ((fd = open(path, O_WRONLY | O_CREAT, 777))==0){
		printf("Falha ao abrir arquivo de gravação.\n");
		exit(1);
	}
	//abre arquivo do gráfico
	grafico = (FILE *) malloc(sizeof (FILE));
	if (!(grafico = fopen("grafico", "w"))){
		printf("Falha ao abrir arquivo de plot.\n");
		exit(1);
	}
	
	peerlen = sizeof(peer);
	
	// hand-shake 	
	rc = recvfrom(s,hand,sizeof(buffer),0,(struct sockaddr *) &peer,(socklen_t *)&peerlen);
	peerlen = sizeof(peer);
	if (!strncmp(hand, "SIN", 3)) {
#ifdef BLA
		printf("Recebeu SIN\n");
		fflush(stdout);
#endif
		strcpy (hand, "SIN+ACK");
		sendto(s,hand, sizeof(hand), 0, (struct sockaddr *) &peer, peerlen );
#ifdef BLA
		printf("Enviou SIN+ACK\n");
		fflush(stdout);
#endif
		rc = recvfrom(s,hand,5,0,(struct sockaddr *) &peer,(socklen_t *)&peerlen);

		if (strncmp(hand, "ACK", 3)){
			printf("Falha no hand-shake, não recebeu o ACK\n");
			exit(1);
		}
#ifdef BLA
		printf("Recebeu ACK\n");
		fflush(stdout);
#endif

	} else {
		printf("Receptor não recebeu SIN para hand-shake\n");
		exit(1);
	}
	
       
	//pega o primeiro pacote, pra depois entrar na repetição pra pegar o resto dos pacotes
#ifdef BLA
	printf("Esperando para receber o arquivo\n");
	fflush(stdout);
#endif
	rc = recvfrom(s,&buffer,sizeof(buffer),0,(struct sockaddr *) &peer,(socklen_t *)&peerlen);
	gettimeofday(&tv, NULL);
	time_antigo = tv;
	seq_antiga = 1;
	pkt_esperado = 1;
#ifdef BLA
	printf("Recebido pacote da sequência %u de tamanho: %d\n",buffer.seq,rc);
	fflush(stdout);
#endif
	 
	while (buffer.seq!=0) {//quando receber 0 acabou a transmissão
		//se o pacote é o esperado, grava no arquivo, senão apenas envia ack
		if (buffer.seq == pkt_esperado) {
			write (fd, &buffer.buffer, buffer.tam);
			pkt_esperado++;
		}
		seq = buffer.seq;
				
		//mandar ACK com o número de sequência.
		sendto(s,&seq, sizeof(unsigned long), 0, (struct sockaddr *) &peer, peerlen );
#ifdef BLA
		printf("Ack do pacote %u enviado.\n", seq);
		fflush(stdout);
#endif
		//recebe próximo pacote
		rc = recvfrom(s,&buffer,sizeof(buffer),0,(struct sockaddr *) &peer,(socklen_t *)&peerlen);
		gettimeofday(&time_novo,NULL);
		//imprime o gráfico
		//pega a diferença de tempo entre a última impressão(do gráfico) e o tempo atual
		temp = ((time_novo.tv_sec * 1000000)+time_novo.tv_usec) - ((time_antigo.tv_sec*1000000)+time_antigo.tv_usec);
		if (temp > GRAFIC_INTERVAL){		
			tempo =(float) (((time_novo.tv_sec*1000000)+time_novo.tv_usec) - ((tv.tv_sec*1000000)+tv.tv_usec))/1000000;
			fprintf(grafico, "%f %u\n", tempo, (QTD_DADOS * seq * 8));
			time_antigo = time_novo;
			seq_antiga = pkt_esperado;
		}
		
		peerlen = sizeof(peer);
#ifdef BLA
		printf("Recebido pacote da sequência %u de tamanho: %d\n",buffer.seq,rc);
		fflush(stdout);
#endif
	 }
#ifdef BLA
	 printf("Enviando ack 0\n");
	 fflush(stdout);
#endif
	 seq = 0;
	 sendto(s,&seq,sizeof(unsigned long), 0, (struct sockaddr *) &peer, peerlen); //envia ack dizendo que recebeu o pacote de finalização.
	 
	 //fecha os arquivos.
	 close (s);
	 close (fd);
	 fclose (grafico);
	 
}
