#include <stdio.h>
#include <stdlib.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

/* Macros relacionadas com o numero maximo de caracteres num nome e com o
   tamanho das matrizes de char necessarias para os guardar: */
#define NOME_MAX 40
#define NOMEMAT_MAX (NOME_MAX + 1)

/* Macro que define o numero maximo de utentes: */
#define UTENTES_MAX 50

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

/* Elimina tudo ate' ao proximo fim-de-linha (como a chamada fflush(stdin),
   so' que esta ultima nao corresponde `a norma ANSI-C, nao funcionando, por
   isso senao em DOS e com alguns compiladores): */
void mataLinha(void)

{
    int c;
    
    while((c = getchar()) != '\n' && c != EOF)
        /* Nada. */;
}

/* Insere mais um utente na matriz, devolve verdadeiro se a matriz sofreu
   alteracoes: */
int insereUtente(Utente utentes[])

{
    int i;

    /* Procura posicao vazia: */
    for(i = 0; i < UTENTES_MAX; i++)
        if(utentes[i].codigo < 0)
            break;
    /* Se nao ha' qualquer posicao livre sair: */
    if(i == UTENTES_MAX)
    {
        fprintf(stderr, "Erro: base de dados cheia!\n");
        return 0;
    }

    /* Introducao dos dados do utente, sem verificacao de repeticoes: */
    do
    {
        printf("Nome: ");
        if(fgets(utentes[i].nome, NOMEMAT_MAX, stdin) == NULL)
        {
            fprintf(stderr, "Erro: lendo nome!\n");
            exit(EXIT_FAILURE);
        }
        if(utentes[i].nome[strlen(utentes[i].nome)-1] == '\n')
            utentes[i].nome[strlen(utentes[i].nome)-1] = '\0';
        else
            mataLinha();                /* para eliminar resto da linha. */
    }
    while(utentes[i].nome[0] == '\0');
    do
    {
        printf("Categoria: ");
        if(scanf("%d", &utentes[i].categoria) != 1)
        {
            fprintf(stderr, "Erro: lendo categoria!\n");
            exit(EXIT_FAILURE);
        }
    }
    while(utentes[i].categoria < CATEG_MIN || utentes[i].categoria >
          CATEG_MAX);
    mataLinha();
    do
    {
        printf("Codigo: ");
        if(scanf("%d", &utentes[i].codigo) != 1)
        {
            fprintf(stderr, "Erro: lendo codigo!\n");
            exit(EXIT_FAILURE);
        }
    }
    while(utentes[i].codigo < 0);
    mataLinha();

    return 1;
}

/* Remove um utente da matriz, devolve verdadeiro se a matriz sofreu
   alteracoes: */
int removeUtente(Utente utentes[])

{
    int i;
    char nome[NOMEMAT_MAX];
    
    /* Pede nome do utente a remover: */
    printf("Nome a remover: ");
    if(fgets(nome, NOMEMAT_MAX, stdin) == NULL)
    {
        fprintf(stderr, "Erro: lendo nome!\n");
        exit(EXIT_FAILURE);
    }
    if(nome[strlen(nome)-1] == '\n')
        nome[strlen(nome)-1] = '\0';

    /* Procura utente: */
    for(i = 0; i < UTENTES_MAX; i++)
        if(strcmp(utentes[i].nome, nome) == 0)
            break;
    if(i == UTENTES_MAX)
    {
        fprintf(stderr, "Erro: nao encontrei \"%s\"!\n", nome);
        return 0;
    }
    
    /* Elimina utente: */
    utentes[i].codigo = VAZIO;

    return 1;
}

/* Mostra a lista dos utentes: */
void listaUtentes(Utente utentes[])

{
    int i;
    
    for(i = 0; i < UTENTES_MAX; i++)
        if(utentes[i].codigo >= 0)
        {
            printf("Nome: %s\n", utentes[i].nome);
            printf("Categoria: %d\n", utentes[i].categoria);
            printf("Codigo: %d\n\n", utentes[i].codigo);
        }
}

/* Le os utentes dum ficheiro, apagando todos os utentes anteriormente
   guardados na matriz, devolve verdadeiro se conseguiu ler: */
int leUtentes(Utente utentes[])

{
    int i;
    FILE *entrada;
    char nome[FILENAME_MAX+1];
    
    /* Pede nome do ficheiro a ler: */
    printf("De que ficheiro pretende ler? ");
    if(fgets(nome, FILENAME_MAX+1, stdin) == NULL)
    {
        fprintf(stderr, "Erro: lendo nome do ficheiro!\n");
        exit(EXIT_FAILURE);
    }
    if(nome[strlen(nome)-1] == '\n')
        nome[strlen(nome)-1] = '\0';

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

    /* Le tudo o que for possivel: */
    for(i = 0; i < UTENTES_MAX; i++)
    {
        /* Repare-se no formato "%d ", no ultimo fscanf, que diz `a funcao
           para eliminar todos os espacos brancos, incluindo fins-de-linha,
           depois de ler o codigo: */
        if(fgets(utentes[i].nome, NOMEMAT_MAX, entrada) == NULL ||
           fscanf(entrada, "%d", &utentes[i].categoria) != 1 ||
           fscanf(entrada, "%d ", &utentes[i].codigo) != 1)
            break;
        if(utentes[i].nome[strlen(utentes[i].nome)-1] == '\n')
            utentes[i].nome[strlen(utentes[i].nome)-1] = '\0';
    }

    /* Limpa todas as posicoes que nao se conseguiu ler: */
    for(; i < UTENTES_MAX; i++)
        utentes[i].codigo = VAZIO;

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

    return 1;
}

/* Guarda lista de utentes num ficheiro (a informacao que existia no
   ficheiro e' descartada), devolve verdadeiro se consegui guardar: */
int guardaUtentes(Utente utentes[])

{
    int i;
    FILE *saida;
    char nome[FILENAME_MAX+1];
    
    /* Pede nome do ficheiro onde guardar: */
    printf("Em que ficheiro pretende guardar? ");
    if(fgets(nome, FILENAME_MAX+1, stdin) == NULL)
    {
        fprintf(stderr, "Erro: lendo nome do ficheiro!\n");
        exit(EXIT_FAILURE);
    }
    if(nome[strlen(nome)-1] == '\n')
        nome[strlen(nome)-1] = '\0';
    
    /* Abre ficheiro: */
    if((saida = fopen(nome, "w")) == NULL)
    {
        fprintf(stderr, "Erro: abrindo ficheiro!\n");
        return 0;
    }

    /* Escreve utentes: */
    for(i = 0; i < UTENTES_MAX; i++)
        /* So' se escrevem as posicoes preenchidas! */
        if(utentes[i].codigo >= 0)
        {
            fprintf(saida, "%s\n", utentes[i].nome);
            fprintf(saida, "%d\n", utentes[i].categoria);
            fprintf(saida, "%d\n", utentes[i].codigo);
        }

    /* Verifica se houve erros de escrita e fecha ficheiro: */
    if(ferror(saida))
    {
        fprintf(stderr, "Erro: escrevendo no ficheiro!\n");
        fclose(saida);
        return 0;
    }
    fclose(saida);

    return 1;
}

/* Limpa matriz de utentes: */
void limpaUtentes(Utente utentes[])

{
    int i;
    
    for(i = 0; i < UTENTES_MAX; i++)
        utentes[i].codigo = VAZIO;
}

/* Funcao de leitura de opcoes: */
int leOpcao(int min, int max)

{
    int n, res;
    
    do
        if((res = scanf("%d", &n)) == EOF)
        {
            fprintf(stderr, "Erro: lendo opcao!\n");
            exit(EXIT_FAILURE);
        }
    while(res != 1 || n < min || n > max);
    mataLinha();

    return n;
}       
            
/* Programa principal: */
int main(void)

{
    Utente utentes[UTENTES_MAX];
    int opcao, alterado = 0;
    
    /* Limpar a matriz de utentes: */
    limpaUtentes(utentes);

    /* No inicio perguntar se quer ler a base de dados de um ficheiro: */
    printf("Deseja abrir um ficheiro? [1 - sim, 0 - nao] ");
    if(leOpcao(0, 1) == 1)
        leUtentes(utentes);
    
    do
    {
        /* Perguntar opcao ao utilizador: */
        printf("1 - Inserir novo utente\n2 - Remover utente\n"
               "3 - Listar utentes\n4 - Ler de ficheiro\n"
               "5 - Guardar em ficheiro\n0 - Sair\nOpcao: ");
        switch(opcao = leOpcao(0, 5))
        {
          case 1:
            if(insereUtente(utentes))
                alterado = 1;
            break;
          case 2:
            if(removeUtente(utentes))
                alterado = 1;
            break;
          case 3:
            listaUtentes(utentes);
            break;
          case 4:
            if(alterado)
            {
                printf("Deseja guardar antes de ler? [1 - sim, 0 - nao] ");
                if(leOpcao(0, 1) == 1)
                {
                    if(guardaUtentes(utentes))
                        alterado = 0;
                }
                else
                    alterado = 0;
            }
            if(!alterado)       /* so' se le se se conseguiu guardar! */
                if(leUtentes(utentes))
                    alterado = 0;
            break;
          case 5:
            if(guardaUtentes(utentes))
                alterado = 0;
            break;
          case 0:
            break;
          default:
            /* Aqui nunca se pode chegar! */
            break;
        }
    }
    while(opcao != 0);
    
    /* Se houve alteracoes perguntar se se quer guardar: */
    if(alterado)
    {
        printf("Deseja guardar antes de sair? [1 - sim, 0 - nao] ");
        if(leOpcao(0, 1) == 1)
            guardaUtentes(utentes);
    }

    return EXIT_SUCCESS;
}