/*
Nome: Mario
Cognome: Montanari
Classe: 3AIN
Data di svolgimento: 23/01/2025
Data di consegna: 25/01/2025

Scrivi un programma in C++ che:

 1. Definisce una struct con i seguenti campi:
    - iniziale nome;
    - iniziale cognome;
    - anno di nascita.

2. Generare un array di 10 elementi di tipo
   struct con valori casuali (caratteri casuali
   fra A e Z estremi compresi; numeri casuali
   fra 1990 e 2010 estremi compresi)

3. Ordinare (e stampare) tale array in tre modi diversi:
    - per cognome crescente;
    - per anno di nascita decrescente;
    - per nome decrescente e, a parità di nome, per cognome decrescente.
*/

#include <iostream>
#include <ctime> // Libreria usata per 'time'
#include <cstdlib> // Libreria usata per 'rand'

#define CAPACITY 10 // Definisce la capacità dell'arry che utilizzeremo
#define SIZE 10 // Definisce la dimensione dell'array che utilizzeremo
#define NUM_MIN 1990 // Minimo anno di nascita
#define NUM_MAX 2010 // Massimo anno di nascita
#define LETT_MIN 65 // Valore nella tabella ASCII che corrisponde ad 'A'
#define LETT_MAX 90 // Valore nella tabella ASCII che corrisponde a 'Z'

using namespace std;

// Definizione della struct
typedef struct {
	char inizialeNome; // Inizializzazione della prima variabile di tipo 'char'
	char inizialeCognome; // Inizializzazione della seconda varibile di tipo 'char'
	int annoDiNascita; // Inizializzazione della terza varibile di tipo 'int'
} Anagrafe; // Nome della struct

void riempiAnagrafe(Anagrafe arr[], int size); // Funzione che riempe gli elementi della stuct
int comparazione1(const void *x, const void *y); // Funzione che ordina due elementi per cognome decrescente
int comparazione2(const void *x, const void *y); // Funzione che ordina due elementi anno di nascita decrescente
int comparazione3(const void *x, const void *y); // Funzione che ordina due elementi per nome decrescente e, a parità di nome, per cognome decrescente
void stampaAnagrafe(Anagrafe arr[], int size); // Funzione che stampa gli elementi della struct


int main(void) {
	srand(time(NULL));
	
	Anagrafe arr[CAPACITY]; // Inizializzazione dell'array del tipo 'struct'
	int size = SIZE; // Inizializzazione della dimensione dell'array
	
	riempiAnagrafe(arr, size); // Chiamata della funzione che riempe gli elementi della stuct
	
	qsort(arr, size, sizeof(Anagrafe), comparazione1); // Funzione che ordina gli elementi per cognome decrescente
	cout << "Primo modo di stampa: " << endl << endl;
	stampaAnagrafe(arr, size); // Chiamata della funzione che stampa gli elementi della struct
	
	qsort(arr, size, sizeof(Anagrafe), comparazione2); // Funzione che ordina gli elementi anno di nascita decrescente
	cout << endl << "Secondo modo di stampa: " << endl << endl;
	stampaAnagrafe(arr, size); // Chiamata della funzione che stampa gli elementi della struct
	
	qsort(arr, size, sizeof(Anagrafe), comparazione3); // Funzione che ordina due elementi per nome decrescente e, a parità di nome, per cognome decrescente
	cout << endl << "Terzo modo di stampa: " << endl << endl;
	stampaAnagrafe(arr, size); // Chiamata della funzione che stampa gli elementi della struct
	
	return 0;
}

// Definizione della funzione che riempe gli elementi della stuct
void riempiAnagrafe(Anagrafe arr[], int size) {
	for (int i = 0; i < size; i++) {
		arr[i].inizialeNome = rand() % (LETT_MAX - LETT_MIN + 1) + LETT_MIN;
		arr[i].inizialeCognome = rand() % (LETT_MAX - LETT_MIN + 1) + LETT_MIN;
		arr[i].annoDiNascita = rand() % (NUM_MAX - NUM_MIN + 1) + NUM_MIN;
	}
}

// Definizione della funzione che ordina due elementi per cognome decrescente
int comparazione1(const void *x, const void *y) {
	const Anagrafe *AnagrafeX = (const Anagrafe *)x;
	const Anagrafe *AnagrafeY = (const Anagrafe *)y;
	
	return AnagrafeX->inizialeCognome - AnagrafeY->inizialeCognome;
}

// Definizione della funzione che ordina due elementi anno di nascita decrescente
int comparazione2(const void *x, const void *y) {
	const Anagrafe *AnagrafeX = (const Anagrafe *)x;
	const Anagrafe *AnagrafeY = (const Anagrafe *)y;
	
	return AnagrafeY->annoDiNascita - AnagrafeX->annoDiNascita;
}

// Definizione della funzione che ordina due elementi per nome decrescente e, a parità di nome, per cognome decrescente
int comparazione3(const void *x, const void *y) {
	const Anagrafe *AnagrafeX = (const Anagrafe *)x;
	const Anagrafe *AnagrafeY = (const Anagrafe *)y;
	
	if (AnagrafeX->inizialeNome == AnagrafeY->inizialeNome) {
		return AnagrafeY->inizialeCognome - AnagrafeX->inizialeCognome;
	} else {
		return AnagrafeX->inizialeNome - AnagrafeY->inizialeNome;
	}
}

// Definizione della funzione che stampa gli elementi della struct
void stampaAnagrafe(Anagrafe arr[], int size) {
	cout << "   Nome" << "      Cognome" << "    Anno di nascita" << endl;
	for (int i = 0; i < size; i++) {
		// Li ordina mettendo nella prima colonna le iniziali dei nomi, poi le iniziali dei cognomi ed infine gli anni di nascita
		cout << "   " << arr[i].inizialeNome << ".        " << arr[i].inizialeCognome << ".         " << arr[i].annoDiNascita << endl;
	}
	cout << endl;
}