/* possibile soluzione all'esame del 26.6.2000*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>

/* per alcuni campi il testo non fissa una lunghezza, ma neanche
 * dice di non farlo, ne fisso io una ragionevole */
#define ML 255

/* nodo dell'albero binario di ricerca */
struct nodo{
  char descrizione[255];
  unsigned long prezzo; /* su 16 bit e' opportuno il long */
  struct nodo *maggiori, *minori;
};

/* prototipi funzioni */
void *Malloc(size_t); 
FILE *Fopen(const char *, const char *);
struct nodo *leggi_riga(FILE *);
struct nodo *inserisci_in_albero(struct nodo *, struct nodo *);
void inorder(struct nodo *);
int ricerca(struct nodo*, char *);
void libera_albero(struct nodo *a);

/* evito l'utilizzo di variabili globali */

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

 /* un puntatore per dati temporanei e uno per
  * la radice dell'albero */
 struct nodo *tmp;
 struct nodo *albero_beni=NULL;
 /* dati negozio (il testo non precisa niente
  * sulla lunghezza massima, ne fisso io una ragionevole */
 char denominazione[ML];
 char p_iva[ML];
 char indirizzo[ML];
 int ii,jj;
 /* file handler per lettura */
 FILE *dati;
 /* comando inserito */
 char code;
 /* prodotto da ricercare */
 char prodotto[ML];
 /* carrello (e' solo il prezzo finale) */
 unsigned long carrello=0;

	
 /* PRIMA PARTE: leggo e interpreto il file */
 dati=Fopen("CASSA.DAT","r");

 /* innanzitutto leggo le prime tre righe 
  * la sintassi di fscanf, mi permette la lettura di tre righe complete */
 ii=fscanf(dati, "%[^\n]\n%[^\n]\n%[^\n]\n", denominazione, p_iva, indirizzo);
 if(ii!=3){
   fprintf(stderr, "Errore in file !\n");
   exit(EXIT_FAILURE);
 }

 while(1){

  tmp=leggi_riga(dati);
  if(!tmp)break;

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

 /* SECONDA PARTE: iterativamente chiedo un tasto
  * ed eseguo l'operazione relativa */
 while(1){
  printf("Inserisci un comando: ");
  code=(char)toupper(getchar()); 
  /* leggo un tasto e lo converto in maiuscolo
   * purtroppo l'ANSI C non contiene funzioni 
   * ``pratiche'' per la
   * lettura di un singolo tasto */
  getchar(); /* mi elimina il \n nel buffer */

  switch(code){
    case 'L':
      printf("\nElenco beni e relativo prezzo:\n\n");
      inorder(albero_beni);
      break;
    case 'A':
      printf("Inserisci prodotto: ");
      fgets(prodotto, 255, stdin);
      /* fgets incamera anche l'\n' che va elminato */
      prodotto[strlen(prodotto)-1]='\0';
      printf("Sto ricercando: %s\n",prodotto);
      ii=ricerca(albero_beni, prodotto);
      if(!ii){
	printf("Attenzione: prodotto non trovato\n");
	printf("Carrello: %ld\n", carrello);
      }
      else{
	printf("Prezzo : %d\n", ii);
	printf("Inserisci quantità: ");
	scanf("%d",&jj);
        getchar(); /* elimino il \n */
	carrello+=ii*jj;
	printf("Carrello: %ld\n", carrello);
      }
      break;
    case 'S':
      printf("\n%s\n", denominazione);
      printf("%s\n", p_iva);
      printf("%s\n", indirizzo);
      printf("TOTALE: %ld\n\n", carrello);
      carrello=0;
      break;
    case 'U':
      libera_albero(albero_beni);
      exit(EXIT_SUCCESS);
    default:
      break;
  }
 }
 return EXIT_SUCCESS; /* codice di uscita senza errori */
}

/* questa funzione legge descrizione del bene e relativo prezzo se
 * non riesce a leggere ritorna NULL */
struct nodo *leggi_riga(FILE *a){
 struct nodo *letto;
 int nb;

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

 /* provo a leggere dal file la descrizione */
 nb=fscanf(a, "%[^#]#",letto->descrizione);

 /* se e' riuscito a leggere la descrizione nb deve essere
  * pari ad uno */
 if(nb!=1){
  free(letto);
  return NULL;
 }

 /* leggo il prezzo del bene */
 fscanf(a,"%ld\n",&letto->prezzo);
 return letto;
}


/* questa funzione inserisce  i dati in un albero binario  e restituisce la
 * radice, il codice ISBN e' usato come chiave */

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->descrizione,new->descrizione);

 /* 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 il bene
 * tramite la descrizione,
 * ne restituisce il prezzo oppure 0 */
int ricerca(struct nodo *a, char *c){
 int confronto;

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

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

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

/* E' un semplice attraversamento (il fatto che sia inorder non
 * e' significativo), mi permette di stampare la lista beni e 
 * relativi prezzi */
void inorder(struct nodo *a){
  if(!a)return;
  inorder(a->maggiori);
  printf("%s - %ld\n", a->descrizione, a->prezzo);
  inorder(a->minori);
}

/* libero le strutture allocate dinamicamente prima di uscire */
void libera_albero(struct nodo *a){
  if(!a)return;
  libera_albero(a->maggiori);
  libera_albero(a->minori);
  free(a);
}

/* 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;
}