/*
          Diploma Universitario in Ingegneria Elettronica
          Diploma Universitario in Ingegneria Informatica

                    Fondamenti di Informatica II

                     Esercizio per Laboratorio

Esercizio di programmazione da svolgere al computer:

Un file  di testo di nome  DOMANDE.TXT contiene un certo  numero di domande
per un  gioco a  quiz. Ogni domanda  e' costituita da  più righe:  la prima
contiene  solo  due  caratteri,  il  simbolo @  e  una  codice  domanda  (3
caratteri); le  successive righe contengono  il testo della domanda  (e non
hanno mai il carattere @ in prima posizione).

Realizzare un programma in  C che, in una prima fase,  legga e memorizzi le
domande e. in una seconda fase, iterativamente chieda un codice e stampi la
relativa domanda.

Esempio di file DOMANDE.TXT: 

@ABC
Chi e' il docente di Fondamenti di Informatica II?
@AAJ
Quanti sbocchi sul mare ha l'Ungheria?
@DER
Cosa simboleggia la lonza, una delle tre belve
che sbarrano il passo a Dante?
@33A
Quale sara' la data del primo giorno del terzo
millenio?
@UQZ
Chi e' il comandante del Nautilus?


Non  si facciano  ipotesi sul  numero massimo  di domande  né sulla 
lunghezza  massima delle domande. Si utilizzi  un albero binario di 
ricerca per la memorizzazione e la ricerca dei dati. */

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

/* lunghezza codice 3 caratteri +1 di fine stringa*/
#define LCOD 3+1
#define LBUFF 100

/* nodo dell'albero binario di ricerca */
struct nodo{
  char codice[LCOD];
  char *domanda;
  struct nodo *maggiori, *minori;
};

/* prototipi funzioni */
void *Malloc(size_t); 
FILE *Fopen(const char *, const char *);
void *Realloc(void *, size_t);
struct nodo *leggi_domanda(FILE *);
struct nodo *inserisci_in_albero(struct nodo *, struct nodo *);
struct nodo *ricerca_domanda(struct nodo *, char *);


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

 /* un puntatore per dati temporanei e uno per
  * la radice dell'albero */
 struct nodo *tmp;
 struct nodo *albero_domande=NULL;
 /* file handler per lettura */
 FILE *dati;
 /* codice domanda da ricercare */
 char code[LCOD+1];
 /* puntatore a domanda recuperata */
 struct nodo *p;
	
 /* PRIMA PARTE: leggo e interpreto DOMANDE.TXT */
 dati=Fopen("DOMANDE.TXT","r");
 while(1){

  /* chiamo una funzione che legge domanda e codice
   * e ritorna NULL qualora siamo a fondo file
   * in questo caso esco dal ciclo */
  tmp=leggi_domanda(dati);
  if(!tmp)break;

  /* inserisco i dati nell'albero binario */
  albero_domande=inserisci_in_albero(albero_domande, tmp);
 }

 /* SECONDA PARTE: iterativamente chiedo un codice
  * e cerco e stampo la domanda relativa */
 while(1){
  printf("Inserisci un codice domanda: ");
  fgets(code, LCOD+1, stdin);

  /* elimino l'invio */
  code[LCOD-1]='\0';
  printf("Sto ricercando %s\n", code);
  p=ricerca_domanda(albero_domande, code);
  if(!p)
   printf("Domanda non trovata !\n");
  else
   printf("Domanda: %s\n",p->domanda);
 }
 return 0;
}

/* questa funzione legge domanda e codice dal file passato
 * se non riesce a leggere ritorna NULL */
struct nodo *leggi_domanda(FILE *a){
 struct nodo *letto;
 int nb,np;
 char c;

 /* alloco struttura per dati */
 letto=Malloc(sizeof(struct nodo));

 /* provo a leggere dal file il codice*/
 nb=fscanf(a, "@%s\n",letto->codice);

 /* se e' riuscito a leggere il codice nb deve essere 1*/
 if(nb!=1){
  free(letto);
  return NULL;
 }

 /* termino la stringa codice */
 letto->codice[LCOD-1]='\0';

 /* inizio a leggere la domanda */
 nb=1;
 np=0;
 letto->domanda=Malloc(LBUFF*sizeof(char));
 c=(char)getc(a);
 /* leggo carattere a carattere fino alla fine del file
  * o fino alla prossima '@' */
 while(c!='@'&&!feof(a)){
  letto->domanda[np++]=c;
  if(np==LBUFF)
   letto->domanda=Realloc(letto->domanda,LBUFF*sizeof(char)*++nb);
  c=(char)getc(a);
 }

 /* se ho letto '@' mi sposto indietro */
 if(c=='@')
  ungetc('@',a);

 /* termino la stringa domanda */
 letto->domanda[np]='\0';

 return letto;
}


/* questa funzione inserisce  i dati in un albero binario  e restituisce la
 * radice */

struct nodo *inserisci_in_albero(struct nodo *tree, struct nodo *new){
 int confronto;

 /* se l'albero non esiste il primo nodo e'
  * sicuramente la radice */
 if(!tree)return new;

 /* confronto i codici per capire come
  * spostarmi lungo i rami */
 confronto=strcmp(tree->codice,new->codice);

 /* se confronto e' nullo ho una chiave duplicata ! */
 if(!confronto){
  fprintf(stderr, "Errore chiave duplicata!\n");
  exit(EXIT_FAILURE);
 }

 /* se confronto  e' <  0 allora la  chiave del nuovo  nodo e'  maggiore di
  * quella del nodo con cui lo stiamo confrontando:
  *
  * se esiste il sottoalbero richiamo ricorsivamente la funzione altrimenti 
  * inserisco direttamente */

 if(confronto<0){
  if(tree->maggiori)
   tree->maggiori=inserisci_in_albero(tree->maggiori, new);
  else{
   tree->maggiori=new;
  }
 }else{
  if(tree->minori)
   tree->minori=inserisci_in_albero(tree->minori, new);
  else{
   tree->minori=new;
  }
 }

 return tree;
}

/* questa procedura ricerca nell'albero binario la domanda
 * e ne restituisce l'indirizzo in menmoria
 * se non la trova rende NULL */
struct nodo *ricerca_domanda(struct nodo *a, char *c){
 int confronto;

 /* se il sottoalbero di ricerca non esiste ritorno
  * NULL */
 if(!a)return NULL;

 /* utilizzo strcmp per confrontare chiave del nodo
  * e chiave che sto cercando */
 confronto=strcmp(a->codice,c);

 /* a seconda del risultato ho trovato la chiave o
  * devo continuare la ricerca */
 if(!confronto) return a;
 if(confronto<0) return ricerca_domanda(a->maggiori, c);
 return ricerca_domanda(a->minori, c);
}

/* solite procedure utilizzate durante il corso */

void *Malloc(size_t size)
{
 void *genericp;
 if((genericp=malloc(size))==NULL)
  {
   perror("Malloc");
   exit(1);
  }
  return genericp;
}

/*************************************************************************/

FILE *Fopen(const char *nome, const char *modo)
{
FILE *tmp;

 if((tmp=fopen(nome, modo))==NULL)
  {
   fprintf(stderr, "Non riesco ad aprire il file %s in maniera %s\n",nome, modo);
   perror("Fopen");
   exit(1);
  }
 return tmp;
}

/*************************************************************************/

void *Realloc(void *ptr, size_t size)
{
 void *genericp;
 if((genericp=realloc(ptr, size))==NULL)
  {
   perror("Realloc");
   exit(1);
  }
  return genericp;
}