#include <stdio.h>
#include <stdlib.h>
//Variaveis globais que guardam os tamanhos minimos e maximos do buffer
int minimo,maximo;
//Estrutura que representa uma fila
typedef struct _fila{
	//Vetor dinamico
	int *vetor;
	//variavel que guarda o tamnho atual do vetor
	int tamanho;
	//guarda a proxima posicao a ser removida
	int remover;
	//guarda a proxima posicao a ser inserida 
	int inserir;
	//guarda o numero de elementos do vetor
	int n_elementos;
}fila;
//Funcao para criar a fila
void criar(fila *f,int tamanho){
	//Aloca posicoes de memoria para o vetor
	f->vetor=(int*)malloc(sizeof(int)*tamanho);
	//Guarda o tamanho do vetor
	f->tamanho=tamanho;
	//Proximo elemento a ser removido, o proximo elemento a ser inserido sera o 0 e o numero de elementos tb eh 0
	f->remover=f->inserir=f->n_elementos=0;
}
//Funcao para destruir a fila
void destruir(fila *f){
	//Libera o espaco de memoria do vetor
	free(f->vetor);
}
//Funcao para dizer que a fila esta vazia
int vazia(fila *f){
    //Retorna se o numero de elementos da fila eh igual a 0
	return f->n_elementos==0;
}
//Funcao para remover um elemento da fila
int remover(fila *f,FILE *saida,char string[]){
    //Se a fila estiver vazia para a execucao da funcao
    if (vazia(f)){
        printf("\nTentativa de retirada invalida do buffer %s, pois esta vazio\n",string);
		return 0;
	}
	//Escreve no arquivo o valor removido
    	fprintf(saida,"%d\n",f->vetor[f->remover]);
    	//Diz indica o proximo elemento a ser removido
    	f->remover++;
    	//Decrementa o numero de elementos da fila
	f->n_elementos--;
	//Se a posicao do proximo elemento a ser removido nao existir, aponta para a posicao 0
	if (f->remover==f->tamanho)
		f->remover=0;
    	//Se a fila estiver subutilizada diminui seu tamanho pela metade
	if ((float)f->n_elementos<(float)f->tamanho/3){
        	if((f->tamanho/2)>=minimo){
            		//Cria um vetor auxiliar e copia para ele o antigo vetor
            		int *tmp=f->vetor;
            		int i;
            		f->tamanho/=2;
            		//Aloca um novo vetor
            		f->vetor=(int *)malloc(sizeof(int)*f->tamanho);
            		//Copia os elementos do vetor auxiliar para o novo vetor na ordem de remocao
            		for (i=0;i<f->n_elementos;i++)
                		f->vetor[i]=tmp[(i+f->remover)%(f->tamanho*2)];
            		free(tmp);
           	 	f->remover=0;
            		f->inserir=f->n_elementos;
           		printf("\nBuffer %s reduzido pela metade por subutilizacao\n",string);
        	}
        	else{
            		printf("\nO buffer %s esta subutilizado mas nao pode ter seu tamanho\n",string);
            		printf("reduzido devido ao limite de tamanho minimo\n");
        	}
    	}        
	return 1;
}
//Funcao para inserir um valor na fila
int inserir(fila *f,int valor,char string[]){
    //Se a fila estiver cheia duplica a fila
    if (f->n_elementos==f->tamanho){
        if (!(f->tamanho*2>maximo)){
           //Cria uma variavel auxiliar e copia o vetor para ela
           int *tmp=f->vetor;
		   int i;
		   f->tamanho*=2;
		   //Aloca um novo vetor
		   f->vetor=(int *)malloc(sizeof(int)*f->tamanho);
		   //Copia os elementos do vetor auxiliar para o novo vetor
		   for (i=0;i<f->n_elementos;i++)
			   f->vetor[i]=tmp[(i+f->remover)%f->tamanho];
	       free(tmp);
	       f->remover=0;
	       f->inserir=f->n_elementos;
	       printf("\nBuffer %s duplicado\n",string);
	     }
	     else{
            printf("\nO valor %d foi descartado devido ao limite do buffer %s ter sido estourado\n",valor,string);
            return 0;
         }
    }
    //Coloca o valor na proxima posicao a ser inserida
    f->vetor[f->inserir]=valor;
    //Aponta a proxima posicao a ser inserida
    f->inserir=(f->inserir==(f->tamanho-1))?(0):(f->inserir+1);
    //Incrementa o numero de elementos
    f->n_elementos++;
    return 1;
}
//Funcao principal		
int main(int argc,char **argv){
	//Verifica se os dados foram entrados corretamente
		if (argc!=4){
		printf("\nDigite na linha de comando o nome do arquivo de entrada e o tamanho minimo e maximo do buffer\n");
		exit(0);
	}
	//Variavel para armazenar os valores recebidos do arquivo
	int numero;
	//Armazenamento dos tamanho minimo e maximo do buffer que foram passados como parametro 
	minimo=atoi(argv[2]);
	maximo=atoi(argv[3]);
	//Declaração das variáveis tipo fila
	fila fila_positivo,fila_negativo;
	//Arquivos de entrada de saida
	FILE *entrada,*saida_pos,*saida_neg;
	if (!(entrada=fopen(argv[1],"r"))){
        printf("\nO arquivo indicado nao pode ser aberto\n");
        exit(0);
    	}
    	//Abertura dos arquivos
	saida_pos=fopen("positivo.out","w");
	saida_neg=fopen("negativo.out","w");
	//Cria as filas por padrao com o tamanho minimo inserido pelu usuario na linha de comando
    	criar(&fila_positivo,minimo);
	criar(&fila_negativo,minimo);
	//Este laco perdura ate o fim do arquivo
	while ((fscanf(entrada,"%i",&numero))!=EOF){
        	//Se o numero for positivo joga no buffer positivo e se bem sucedido nisso retira o valor do buffer negativo.
        	if (numero>=0){
            		if(inserir(&fila_positivo,numero,"positivo"))
                		remover(&fila_negativo,saida_neg,"negativo");
        	}
        	//Se o numero for negativo joga no buffer negativo e se bem sucedido tenta retirar um valor do buffer positivo
        	else{
            		if(inserir(&fila_negativo,numero,"negativo"))
                		remover(&fila_positivo,saida_pos,"positivo");
        	}
    	}
   	 destruir(&fila_positivo);
    	 destruir(&fila_negativo);
    	 fclose(entrada);
         fclose(saida_neg);
    	 fclose(saida_pos);
	return 0;
}

