#include <stdio.h>
#include <stdlib.h>

#include "leituras.h"

/* Macros relacionadas com as categorias admissiveis: */
#define CATEG_MIN 0
#define CATEG_MAX 2

/* Macro que define um valor possivel para marcar posicoes vazias: */
#define VAZIO -1

/* Numero maximo de caracteres num nome: */
#define NOME_MAX 40

/* Definicao da estrutura que guarda informacao acerca de um utente: */
typedef struct Utente
{
    char nome[NOME_MAX+1];
    int categoria;
    int codigo;
} Utente;

/* Definicao de um tipo para assinalar o resultado das funcoes: */
typedef enum {mau, bom} resultado;

/* Registo vazio tipo: */
const Utente vazio = {"", 0, VAZIO};

resultado insereUtente(FILE *f)

{
    long pos, posvazia;
    Utente utente, novo;
    
    /* Le dados do novo utente do teclado, sem verificacao de repeticoes: */
    LEnome("Nome: ", novo.nome, NOME_MAX);
    novo.categoria = LEvalor("Categoria: [%d a %d] ", CATEG_MIN, CATEG_MAX);
    novo.codigo = LEvalor("Codigo: [>=%d] ", 0, 0);

    rewind(f);
    
    /* Procura posicao vazia e verifica repeticoes: */
    posvazia = -1;
    while(pos = ftell(f), fread(&utente, sizeof(Utente), 1, f) == 1)
	if(utente.codigo == VAZIO)
	    posvazia = pos;
	else
	    if(utente.codigo == novo.codigo)
	    {
		printf("O codigo %d ja' existe!\n", novo.codigo);
		return mau;
	    }
	    else if(strcmp(utente.nome, novo.nome) == 0)
	    {
		printf("O nome \"%s\" ja' existe!\n", novo.nome);
		return mau;
	    }
    
    /* Se houver alguma posicao livre coloca posicao escrita: */
    if(posvazia != -1)
	fseek(f, posvazia, SEEK_SET);
    
    /* Escreve o novo utente: */
    if(fwrite(&novo, sizeof(Utente), 1, f) != 1)
    {
        fputs("Erro: guardando novo utente!\n", stderr);
        exit(EXIT_FAILURE);
    }

    return bom;
}


int removeUtente(FILE *f)

{
    char nome[NOME_MAX+1];
    Utente utente;
    long pos;
    
    /* Pede nome do utente a remover: */
    LEnome("Nome a remover: ", nome, NOME_MAX);

    rewind(f);
    
    /* Procura o utente: */
    while(pos = ftell(f), fread(&utente, sizeof(Utente), 1, f) == 1)
	if(utente.codigo != VAZIO && strcmp(utente.nome, nome) == 0)
	    break;

    if(feof(f))
    {
	fprintf(stderr, "Erro: utente \"%s\" nao exite!\n", nome);
	return mau;
    }

    fseek(f, pos, SEEK_SET);

    /* Sobrepoe utente "vazio": */
    if(fwrite(&vazio, sizeof(Utente), 1, f) != 1)
    {
        fputs("Erro: escrevendo vazio!\n", stderr);
        exit(EXIT_FAILURE);
    }
    return bom;
}


void listaUtentes(FILE *f)

{
    Utente utente;
    
    rewind(f);
    
    /* Procura o utente: */
    while(fread(&utente, sizeof(Utente), 1, f) == 1)
	if(utente.codigo != VAZIO)
            printf("Nome: %s\nCategoria: %d\nCodigo: %d\n\n", 
		   utente.nome, utente.categoria, utente.codigo);
}


resultado leUtentes(FILE *f)

{
    FILE *entrada;
    char nome[FILENAME_MAX+1];
    Utente utente;
    long pos;
    
    /* Pede nome do ficheiro a ler: */
    LEcadeia("De que ficheiro pretende ler? ", nome, FILENAME_MAX);

    /* Abre ficheiro: */
    if((entrada = fopen(nome, "r")) == NULL)
    {
        fprintf(stderr, "Erro: abrindo ficheiro \"%s\"!\n", nome);
        return mau;
    }

    rewind(f);
    
    /* Le tudo o que for possivel e escreve no ficheiro de registos: */
    while(fgets(utente.nome, NOME_MAX+1, entrada) != NULL &&
	  fscanf(entrada, "%d %d ", &utente.categoria, &utente.codigo) == 2)
    {
        if(utente.nome[strlen(utente.nome)-1] == '\n')
            utente.nome[strlen(utente.nome)-1] = '\0';
	fwrite(&utente, sizeof(Utente), 1, f);
    }

    /* Fecha ficheiro de entrada: */
    fclose(entrada);

    /* Limpa todas as posicoes que faltam no ficheiro de registos: */
    while(pos = ftell(f), fread(&utente, sizeof(Utente), 1, f) == 1)
	if(utente.codigo != VAZIO)
	{
	    fseek(f, pos, SEEK_SET);
	    fwrite(&vazio, sizeof(Utente), 1, f);
	}

    if(ferror(f))
    {
        fputs("Erro: escrevendo no ficheiro de registos!\n", stderr);
        exit(EXIT_FAILURE);
    }

    return bom;
}


resultado guardaUtentes(FILE *f)

{
    FILE *saida;
    char nome[FILENAME_MAX+1];
    Utente utente;
    
    /* Pede nome do ficheiro onde guardar: */
    LEcadeia("Em que ficheiro pretende guardar? ", nome, FILENAME_MAX);

    /* Abre ficheiro: */
    if((saida = fopen(nome, "w")) == NULL)
    {
        fprintf(stderr, "Erro: criando ficheiro \"%s\"!\n", nome);
        return mau;
    }

    rewind(f);

    /* Escreve utentes: */
    while(fread(&utente, sizeof(Utente), 1, f) == 1)
        if(utente.codigo != VAZIO)
            fprintf(saida, "%s\n%d\n%d\n", 
		    utente.nome, utente.categoria, utente.codigo);

    if(ferror(saida))
    {
        fprintf(stderr, "Erro: escrevendo no ficheiro de texto!\n");
        fclose(saida);
        return mau;
    }
    fclose(saida);

    return bom;
}


int main(int argc, const char * const *argv)

{
    FILE *f;			/* ficheiro de registos. */
    int opcao;
    const char *nome;
    
    nome = argc == 1? "utentes.db" : argv[1];

    /* Tenta abrir ficheiro de registos ja' existente, se nao existir tenta
       criar um novo: */
    if((f = fopen(nome, "r+b")) == 0 && (f = fopen(nome, "w+b")) == 0)
    {
	fprintf(stderr, "Erro: nao consegui abrir nem criar \"%s\"!",nome);
	return EXIT_FAILURE;
    }
    
    do
    {
        /* Perguntar opcao ao utilizador: */
        printf("\n1 - Inserir novo utente\n2 - Remover utente\n"
               "3 - Listar utentes\n4 - Ler de ficheiro\n"
               "5 - Guardar em ficheiro\n0 - Sair\n\n");
        switch(opcao = LEvalor("Opcao: [%d a %d] ", 0, 5))
        {
          case 1:
            insereUtente(f);
            break;
          case 2:
            removeUtente(f);
            break;
          case 3:
            listaUtentes(f);
            break;
          case 4:
	    leUtentes(f);
            break;
          case 5:
            guardaUtentes(f);
            break;
          case 0:
            break;
          default:
            /* Aqui nunca se pode chegar! */
            break;
        }
    }
    while(opcao != 0);
    
    fclose(f);

    return EXIT_SUCCESS;
}