/* Svolgimento compito C del 1-7-99 (Ambiente e Territorio), a cura del docente */

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

#define EPS 1E-6
/* se il modulo dalla differanza di due numeri e` inferiore a EPSilon i due numeri
   sono considerati eguali */

#define MAXNOMEFILE 21
/* 20 caratteri piu` terminatore */

typedef char nomefile[MAXNOMEFILE];

#define NRIG 20
#define NCOL 20
/* per sovradimensionare le matrici */

struct structmatrice {
	int nrig, ncol;
	float mat[NRIG][NCOL];
};

typedef struct structmatrice matrice;

struct elista {
	nomefile info;
	struct elista *next;
};

typedef struct elista elem;

/* UNICHE VAR GLOBALI -- EVENTO ECCEZIONALE */
elem *Lista=NULL,  /* PUNTA ALLA LISTA DEI NOMI DI FILE DI CUI AL PUNTO 3. */
     *Coda=NULL;

/* prototipi */
int main(void);
elem *CercaInLista(nomefile);
void DistruggiLista();
int Lavora();
void InserisciInCoda(nomefile);
void PrintMenu();
int CostruisciFile();
int LeggiMatrice(matrice*);
void StampaMatrice(char *, matrice);
int ScaricaMatrice(matrice);
void StampaLista();
void LeggiNomeFile(char *, nomefile);
void CostruisciLista();
void EmendaLista(nomefile);
void AnalizzaMinore();
int Confronta(nomefile, nomefile, int *, int *);
int CaricaMatrice(nomefile, matrice*);
void CostruisciMinore(matrice, matrice*, int, int);
int ConfrontaMatrici(matrice, matrice);
int AnalizzaLista();
int VerificaFile(nomefile);
void DistruggiElemento(elem *);

int main(void)
{
	printf ("Benvenuti!\n\n");
	
	while(Lavora()==1);
	
	printf ("\nArrivederci!\n");

	return 0;
}


int Lavora()
/* restituisce 0 se utente ha chiesto terminazione, 1 altrimenti */
{
	int scelta;
	PrintMenu();
	scelta=getchar();
	getchar();
	switch(scelta) {
		case '1':
					CostruisciFile();
					return 1;
		case '2':
					CostruisciLista();
					return 1;
		case '3':
					AnalizzaMinore();
					return 1;
		case '4':
					AnalizzaLista();
					return 1;
		case '5':
					return 0;
		default :
					printf("\n\nscelta non valida...\n");
					return 1;
	}
}


void PrintMenu()
{
	printf("MENU\n\n");
	printf("\t[1] Costruisci file con matrice.\n\n");
	printf("\t[2] Costruisci lista nomi file.\n\n");
	printf("\t[3] Determina se una matrice e` un minore di un'altra matrice (punto 2 richiesto).\n\n");
	printf("\t[4] Analizza lista nomi file (punto 3 richiesto).\n\n");
	printf("\t[5] Termina.\n\n");
	printf("\nInserisci scelta... ");
	return;
}


int CostruisciFile()
/* 
   restituisce 0 se ha successo, un numero > 0 se fallisce:
   1 == fallimento lettura matrice
   2 == fallimento costruzione file
*/
{
	matrice m;
	
	if(LeggiMatrice(&m)==1)
		return 1;	
	
	if(ScaricaMatrice(m)==1)
		return 2;
	
	printf("Costruzione file terminata con successo.\n");
	return 0;
}

int LeggiMatrice(matrice *z)
/* ritorna 0 se l'operazione ha successo, 1 altrimenti */
{
	int i,j;
	
	printf("\n\nInserimento matrice...");
	printf("Immettere numero righe e numero colonne: ");
	scanf("%d%d", &z->nrig, &z->ncol);
	if((z->nrig>NRIG)||(z->ncol>NCOL)) {
		printf("\nMatrice troppo grande (max %dx%d). Operazione annullata.\n\n",NRIG,NCOL);
		return 1;
	}
	
	printf("Immissione matrice (scrivere valori separati da spazi e/o invii):\n");
	for(i=0; i<z->nrig; i++)
		for(j=0; j<z->ncol; j++)
			scanf("%f",&z->mat[i][j]);
	getchar();
	printf("Immissione matrice terminata.\n\n");
	
	StampaMatrice("Matrice inserita:",*z);
	
	return 0;
}


void StampaLista()
{
	elem *cursore=Lista;

	printf("Inizio lista:\n");	
	while(cursore!=NULL) {
		printf("%s\n",cursore->info);
		cursore=cursore->next;
	}
	printf("Fine lista.\n\n");
	return;
}

void StampaMatrice(char *messaggio, matrice m)
{
	int i,j;
	printf("%s\n", messaggio);
	for(i=0; i<m.nrig; i++) {
		for(j=0; j<m.ncol; j++)
			printf("%12.4f ",m.mat[i][j]);
		printf("\n");
	}
	return;
}


int ScaricaMatrice(matrice m)
/* restituisce 0 se ha successo, 1 se fallisce */
{
	nomefile nome;
	FILE *f;
	
	LeggiNomeFile("Inserisci nome file (.dat) scelto per memorizzare la matrice: ",nome);
	f=fopen(nome,"wb");
	if(f==NULL) {
		printf("Fallimento nell'apertura del file \"%s\" in modalita` \"wb\". Operazione annullata.\n",nome);
		return 1;
	}
	
	/* scarica prima le dimensioni */
	fwrite(&m.nrig,sizeof(m.nrig),1,f);
	fwrite(&m.ncol,sizeof(m.ncol),1,f);
	
	/* scarica tutta la matrice */
	fwrite(&m.mat,sizeof(m.mat),1,f);
	
	fclose(f);
	return 0;
}


void LeggiNomeFile(char *messaggio, nomefile nome)
{
	printf("%s", messaggio);
	gets(nome);
	return;
}


void CostruisciLista()
{
	int scelta, inserisco;
	nomefile nome;
	
	if(Lista != NULL) {
		printf("Una lista esiste gia`.\n");
		StampaLista();
		printf("Premi [7] per crearne una nuova (distruggendo la vecchia).\n\n");
		printf("Premi [8] per aggiungere in coda alla vecchia.\n\n");
		printf("Premi [9] per annullare l'operazione.\n\n\n");
		scelta=getchar();
		getchar();
		switch(scelta) {
			case '7':	DistruggiLista();
						break;
			case '8':	break;
			case '9':	return;
			default :	printf("\n\nscelta non valida. Operazione annullata.\n\n");
						return;
		}
	}
	
	printf("Immetti i nomi dei file separati da invii -- premi invio a vuoto per terminare\n");
	LeggiNomeFile("nome file: ",nome);
	while(nome[0]!=0) { /* una stringa vuota contiene solo il terminatore */
		if(CercaInLista(nome)!=NULL)
			printf("File \"%s\" gia` presente in lista - non verra` inserito.\n",nome);
		else {
			inserisco=VerificaFile(nome); /* 0 se il file esiste ed e` apribile, 1 altrimenti */
			if(inserisco==1) {
				printf("Il file \"%s\" non puo` essere aperto in modalita` \"rb\".\n",nome);
				printf("[8] Forza inserimento.\n\n");
				printf("[9] Annulla inserimento.\n\n\n");
				scelta=getchar();
				getchar();
				switch(scelta) { /* occhio a come e` scritta questa istruzione! */
					case '8': inserisco=0;
							  break;
					default : printf("\n\nscelta non valida. Operazione annullata.\n\n");
					case '9': break;
				}
			}
			if(inserisco==0)
				InserisciInCoda(nome);
		}
		LeggiNomeFile("nome file: ",nome);
	}
	
	return;
}


elem *CercaInLista(nomefile nome)
/* restituisce l'indirizzo di nome se presente, NULL altrimenti */
{
	elem *cursore;
	
	cursore=Lista;
	while(cursore!=NULL)
		if(strcmp(nome,cursore->info)==0)
			return cursore;
		else
			cursore=cursore->next;
	return NULL;
}
	


int VerificaFile(nomefile nome)
/* restituisce 0 se il file e` apribile "rb", 1 altrimenti */
{
	FILE *f;
	printf("Verifico file \"%s\".\n", nome);
	f=fopen(nome,"rb");
	if(f!=NULL) {
		fclose(f);
		return 0;
	} else
		return 1;
}


void DistruggiLista()
{
	elem *morituro;
	
	printf("Inizio distruzione lista... ");
	while(Lista != NULL) {
		morituro=Lista;
		Lista=Lista->next;
		free(morituro);
	}
	printf("fatto.\n\n");
	return;
}


void InserisciInCoda(nomefile nome)
{
	elem *nuovo;
	
	printf("Inserimento nome file \"%s\"... ", nome);
	nuovo=malloc(sizeof(elem));
	if(nuovo==NULL) {
		printf("Memoria esaurita. Impossibile inserire.\n\n");
		return;
	}
	
	strcpy(nuovo->info,nome);
	nuovo->next=NULL;
	if(Lista==NULL)
		Lista=nuovo;
	else
		Coda->next=nuovo;
	Coda=nuovo;
	printf("fatto.\n");
	return;
}

void AnalizzaMinore()
{
	nomefile nome1, nome2;
	int i, j;
	
	printf("Analisi minori.\n");
	LeggiNomeFile("Nome PRIMO file: ", nome1);
	LeggiNomeFile("Nome SECONDO file: ", nome2);
	
	if(Confronta(nome1, nome2, &i, &j) == 1)
		printf("Responso positivo. Indici: (%d,%d)\n\n",i,j);
	else
		printf("Responso negativo.\n\n");
	
	printf("\nFine analisi minori.\n");
	return;
}


int Confronta(nomefile nome1, nomefile nome2, int *riga, int *colonna)
/* 
   Restituisce 1 se la mat. in nome2 e` un minore della matrice in nome1;
   in tal caso restituisce in i e j gli indici che definiscono il minore.
   Restituisce 0 altrimenti.
   E` la funzione richiesta al punto 2 del testo.
*/
{
	matrice m1, m2, m3;
	
	
	if(CaricaMatrice(nome1, &m1)==1) {
		printf("Impossibile caricare matrice dal file \"%s\".\n", nome1);
		return 0;
	}
	if(CaricaMatrice(nome2, &m2)==1) {
		printf("Impossibile caricare matrice dal file \"%s\".\n", nome2);
		return 0;
	}

	if((m1.nrig != m2.nrig+1) || (m1.ncol != m2.ncol+1))
		return 0;
	
	printf("Confronto file \"%s\" e \"%s\".\n",nome1, nome2);
	printf("Matrici:\n");
	StampaMatrice("PRIMA matrice:",m1);
	StampaMatrice("\nSECONDA matrice:",m2);
	
	for(*riga=1; *riga <= m1.nrig; (*riga)++)
		for(*colonna=1; *colonna <= m1.ncol; (*colonna)++) {
			CostruisciMinore(m1, &m3, *riga, *colonna);
			if(ConfrontaMatrici(m2, m3) == 0) /* 0 = eguaglianza delle matrici */
				return 1;
		}
	return 0;
}


int CaricaMatrice(nomefile nome, matrice *m)
/* restituisce 0 se ha successo, 1 altrimenti */
{
	FILE *f;
	f=fopen(nome,"rb");
	if(f==NULL) {
		printf("Impossibile aprire file \"%s\" in modalita` \"rb\".\n",nome);
		return 1;
	}
	fread(&m->nrig, sizeof(m->nrig), 1, f); /* legge prima n.ro righe */
	fread(&m->ncol, sizeof(m->ncol), 1, f); /* n.ro colonne */
	if((m->nrig>NRIG)||(m->ncol>NCOL)) {
		printf("\nMatrice in file \"%s\" troppo grande (max %dx%d). Operazione annullata.\n\n",nome,NRIG,NCOL);
		return 1;
	}
	fread(&m->mat, sizeof(m->mat), 1, f); /* matrice effettiva */
	fclose(f);
	return 0;
}


void CostruisciMinore(matrice grande, matrice *piccola, int r, int c)
/* attenzione, i valori di r (riga) e c (colonna) sono quelli "matematici".
   In C gli indici iniziano da 0, per cui i valori di interesse in pratica sono 
   r-1 e c-1
*/
{
	int i, j, /* indici per scandire la matrice grande */
	    ii, jj; /* per scandire la matrice piccola */
	    
	r--; c--; /* corregge prima gli indici */
	piccola->nrig=grande.nrig-1;
	piccola->ncol=grande.ncol-1;
	for(ii=0; ii < piccola->nrig; ii++)
		for(jj=0; jj < piccola->ncol; jj++) {
			if(ii<r)   /* siamo sopra alla riga da cancellare */
				i=ii;
			else       /* siamo sotto alla riga da cancellare */
				i=ii+1;
			if(jj<c)   /* siamo sopra alla colonna da cancellare */
				j=jj;
			else       /* siamo sotto alla colonna da cancellare */
				j=jj+1;
			piccola->mat[ii][jj]=grande.mat[i][j];
		}
	return;
}


int ConfrontaMatrici(matrice m1, matrice m2)
/* restituisce 0 se identiche, 1 altrimenti */
{
	int i, j;
	
	/* in realta` il test seguente e` inutile poiche'
	   la funzione viene sempre chiamata su matrice di eguali dimensioni */
	if((m1.nrig!=m2.nrig)||(m1.ncol!=m2.ncol)) 
		return 1;

	for(i=0; i < m1.nrig; i++)
		for(j=0; j< m1.ncol; j++)
			/* per il confronto fra razionali e` bene usare una soglia EPSilon */
			if((m1.mat[i][j]-m2.mat[i][j]<=-EPS)||(m1.mat[i][j]-m2.mat[i][j]>=EPS))
				return 1;

	return 0;
}


int AnalizzaLista()
/* restituisce 0 in caso di successo, un positivo altrimenti */
{
	nomefile nome;
	
	if(Lista==NULL) {
		printf("La lista non esiste. Operazione annullata.\n");
		return 1;
	}
	
	LeggiNomeFile("Inserisci nome file riferimento per il confronto con la lista: ",nome);
	if(VerificaFile(nome)==1) {
		printf("Il file \"%s\" non e` apribile in modalita` \"rb\". Operazione annullata.\n",nome);
		return 2;
	}
	EmendaLista(nome);
	return 0;
}


void EmendaLista(nomefile nome)
/* funzione richiesta al punto 3. */
{
	elem *cursore, *prec;
	int i, j; /* sono richiesti per la chiamata di Confronta */
	
	printf("Inizio emendamento della seguente lista:\n");
	StampaLista();
	prec=NULL;
	cursore=Lista;
	while(cursore!=NULL) {
		if(Confronta(nome,cursore->info,&i,&j)==0) 
			DistruggiElemento(prec);
		else
			prec=cursore;
		if(prec==NULL)
			cursore=Lista;
		else
			cursore=prec->next;
	}
	printf("Fine emendamento. Lista risultante:\n");
	StampaLista();
	return;
}


void DistruggiElemento(elem *prec)
{
	elem *morituro;
	
	if(prec==NULL) {
		morituro=Lista;
		Lista=Lista->next;
	} else {
		morituro=prec->next;
		prec->next=prec->next->next;
	}
	free(morituro);
	return;
}