/* mastersel.c	Master daemon using select
 * 
 * Example of master using TCP protocol.
 * Reference: W. Richard Stevens. "UNIX Network Programming". 
 *	Prentice Hall Software Series. 1990.
 *	Chapter 6: Berkeley Sockets.
 *
 * $Id: master.c,v 1.4 2003/08/03 22:41:30 tmacam Exp $
 *
 */


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

#include "common.h"


#define HOST_ADDR_SIZE 15
#define SERV_HOST1_ADDR "150.164.6.102"	/* host addr for slave 1 */
#define SERV_HOST2_ADDR "150.164.3.12"	/* host addr for slave 2 */
#define MAX_NUM_SLAVES 4		/* Numero máximo de slaves*/
#define MAX_APPROX 3			/* Valor máximo para o numero de erros*/

/* Variáveis globais */
int NUM_SLAVES = 1;			/* numero de escravos */
slave_ctl slaves[MAX_NUM_SLAVES];	/* Escravos ( estrutura e controle ) */
unsigned char pattern[MAX_PATTERN_LEN];	/* padrão de busca */
int approx_k = 0;			/* Numero de erros permitido */



void print_usage()
{
        printf("Uso: master ERROS PADRÃO ARQUIVO1 IP_ESCRAVO1 ... arqN ip_escravoN\n");
        printf("Conta em paralelo o numero de ocorrências de PADRAO em arquivos\n");
        printf("Exemplo: master 2 'hello world' hello.c 127.0.0.1\n\n");
	printf("\tERROS: numero de erros permitido na busca. Entre 0 e %i.\n",MAX_APPROX);
	printf("\tpadrão: o padrão de busca procurado\n");
	printf("\tarquivoK: nome do arquivo a ser pesquisado com o K-ésimo escravo\n");
	printf("\tip_escravoK: IP do K-ésimo escravo\n\n");
}

void parse_cmd_line(int argc, char* argv[])
{
	int i=0;

	/* verifica o numero de mínimo de argumentos */
	if ( argc < 5 ){
        	fprintf(stderr,"master: Número insuficiente de argumentos.\n\n");
        	print_usage();
        	exit(1);
	}

	/* obtem o numero de erros */
	approx_k = atoi(argv[1]);
	if ((approx_k < 0) || (approx_k > MAX_APPROX)){
        	fprintf(stderr,"master: O valor para o número de erros deve entre 0 e %i.\n\n", MAX_APPROX);
        	print_usage();
        	exit(1);
	}

	/*obtem o o padrão de busca */
	if (strlen(argv[2]) >= MAX_PATTERN_LEN){
        	fprintf(stderr,"master: O padrão e busca é muito grande para ser processado.\n\n");
        	print_usage();
        	exit(1);
	}
	if (strlen(argv[2]) <= approx_k){
        	fprintf(stderr,"master: O tamanho do padrão de busca deve ser maior do que o numero de erros.\n\n");
        	print_usage();
        	exit(1);
	}
	strcpy(pattern,argv[2]);

	/*obtem os nomes dos arquivos e os ips dos escravos*/
	if (argc % 2 == 0){
        	fprintf(stderr,"master: A quantidade de arquivos e de escravos difere.\n\n");
        	print_usage();
        	exit(1);
	}
	NUM_SLAVES=0;
	for(i = 3; i < argc; i += 2){
		/* Nome do arquivo */
		if (strlen(argv[i]) >= MAX_FILENAME_LEN){
			fprintf(stderr,"master: O tamanho do nome do %i.o arquivo é muito grande.\n\n", NUM_SLAVES+1);
			print_usage();
        		exit(1);
		}
		strcpy(slaves[NUM_SLAVES].filename, argv[i]);
		/* Endereço do escravo */
		bzero((char *) &(slaves[NUM_SLAVES].address), sizeof(slaves[NUM_SLAVES].address));
		slaves[NUM_SLAVES].address.sin_family = AF_INET;
		slaves[NUM_SLAVES].address.sin_port = htons(SERV_TCP_PORT);
		slaves[NUM_SLAVES].address.sin_addr.s_addr = inet_addr(argv[i+1]);
		if (slaves[NUM_SLAVES].address.sin_addr.s_addr == -1 ){ /* erro... */
        		fprintf(stderr,"master: O endereço ip do %io. escravo não é válido.\n\n",NUM_SLAVES+1);
        		print_usage();
	        	exit(1);
		}
		NUM_SLAVES++;
	}
}

/*
 *
 * limpa o active_fds com um FD_ZERO!!!
 *
 * */
void connect_to_slaves(fd_set *active_fds, int *max_fd)
{
	int i;
	
	/* Inicia o active_fds */
	FD_ZERO(active_fds);
	*max_fd = 0;
	
	for(i = 0; i < NUM_SLAVES; i++) {
		/* Open a TCP socket (an Internet stream socket). */
		if ( (slaves[i].socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
			fprintf(stderr,"master: can't open stream socket\n");
		}
		
		/* Connect to the slave. */
		if ( connect(	slaves[i].socketfd,
				(struct sockaddr *) &(slaves[i].address),
				sizeof(slaves[i].address)
			    ) < 0) {
			fprintf(stderr,"master: can't connect to slave #%i.\n", i);
			exit(1);
		}
		
		/* adds this connection to active_fds and updates max_fd*/
		FD_SET(slaves[i].socketfd, active_fds);
		*max_fd = (slaves[i].socketfd > *max_fd) ? slaves[i].socketfd : *max_fd;
	}
}

void send_request(slave_ctl slaves[], int num_slaves, int approx_k, unsigned char* pattern)
{
	/* Local variables def. - duh!*/
	int i = 0;
	slave_request request;
	int request_len = sizeof(request);

	/*Prepara a parte commum do pedido dos escravos*/
	request.approx_k = approx_k;
	strcpy(request.pattern,pattern);
	
	/* Envia o pedido para cada escravo*/
	for(i = 0; i < num_slaves; i++){
		/* Prepara a parte específica de cara pedido */
		strcpy(request.filename,slaves[i].filename);

		/* Envia */
		if (sendall(slaves[i].socketfd, (void *) &request, &request_len) < 0){
			fprintf(stderr,"master: can't send request to slave #%i.\n",i);
			close(slaves[i].socketfd);
			exit(1);
		}			
	}
}

unsigned long int process_slave_results(slave_ctl slaves[], int num_slaves, fd_set *active_fds)
{
	unsigned long int total_matches_found = 0;	/* Numero total de ocorrências */
	unsigned long int total_slave_time =0;	/* tempo usado nos clientes*/
	unsigned int slaves_ack = 0;		/* Numero de escravos que responderam */
	slave_response response;
	fd_set read_fds;
	int retval;	
	int i;
	
	while (slaves_ack<num_slaves){
		/* Seleciona canais com respostas dos escravos */
		read_fds = *active_fds;
		retval = select(FD_SETSIZE, &read_fds, NULL, NULL, NULL);
		if (retval < 0){
			fprintf(stderr,"master: error on select\n");
			exit(1);
		}

		/* Procura por canais marcados e trata suas respostas*/
		for ( i=0 ; i < num_slaves ; i++){
			if (FD_ISSET(slaves[i].socketfd, &read_fds)){
				/*  Receive the response from the slave. */
				if (recv(slaves[i].socketfd, (void *) &response, sizeof(response),MSG_WAITALL|MSG_NOSIGNAL) < 0){
						fprintf(stderr,"master: can't receive response\n");
						close(slaves[i].socketfd);
						exit(1);
				}

				/* Fecha o canal de comunicação com o escravo */
				FD_CLR(slaves[i].socketfd,active_fds);
				close(slaves[i].socketfd);

				/*Atualiza estatisticas*/
				printf("master: response from slave[%i]\tmatches=%li\ttime=%'li \n",i,response.matches_found,response.process_time);
				total_matches_found += response.matches_found;
				total_slave_time += response.process_time;
				
				slaves_ack++;	/* Mais um escravo terminou*/
			}
		}
	}

	printf("master: total_slave_time=%li\n", total_slave_time);

	return total_matches_found;
}



/***********************************************/
/* Function: main */
/**********************************************/
int main(int argc, char *argv[])
{
	fd_set activefdSet;		/* Guarda a lista dos fd/sockets em uso */
	unsigned int max_fd = 0;	/* maior fd usado no activefdSet */
	unsigned long int matches_found = 0;	/* Numero de ocorrências encontradas*/
	unsigned long start_time, used_time;

	/* Inicia o cronômetro*/
	start_time = get_long_time();	
	
	/* Trata a linha de comando */
	parse_cmd_line(argc, argv);

	/* Conecta-se com o escravos */
	connect_to_slaves(&activefdSet, &max_fd);

	/* Envia os pedidos aos escravos  */
	send_request(slaves, NUM_SLAVES, approx_k, pattern); 

	/* Processa o resultado dos escravos */
	matches_found=process_slave_results(slaves, NUM_SLAVES, &activefdSet);

	/* Contabiliza o tempo */
	used_time = get_long_time() - start_time;
	printf("master: matches_found=%li\tused_time=%li\n",matches_found,used_time);


	return(0);
}
