#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

class Personne {
	int Id;
	string Nom;

public:
	using SAISIE = int;

	Personne() {
		static int i = 0;

		Id = ++i;
	}

	Personne(int Id) : Id(Id) {};

	void saisie() { getline(cin, Nom, ' '); }
	void afficher() { cout << left << setw(3) << Id << left << setw(8) << Nom; }

	// paramètre saisie n'est pas utilisé par défaut
	static SAISIE parametre_saisie() { return 0; }
	static string nomclasse() { return "personne"; }

	bool operator==(string nom) { return Nom == nom; }
	
	bool operator<(Personne& b) const noexcept { return Nom < b.Nom; }
};



class Prof : public Personne {
	string service;

	
public:
	static int i;
	using SAISIE = char;

	Prof() : Personne(++i) {};

	void saisie(SAISIE n) { Personne::saisie(); getline(cin, service, n); }
	void afficher() { Personne::afficher(); cout << left << setw(8) << service; }

	static string nomclasse() { return "prof"; }
	static SAISIE parametre_saisie() {return '\n';}
};

class Direction : public Prof {
	string Titre;

public:


	void saisie() { Prof::saisie(' '); getline(cin, Titre); }
	void afficher() { Prof::afficher(); cout << left << setw(8) << Titre; }

	static string nomclasse() { return "direction"; };

	template <class C>
	void moyennes(C& etudiants) {
		cout << "==Moyennes==" << endl;
		for (auto etud : etudiants) {
			cout << endl;
			etud.afficher();

			etud.Moy = 0;
			for (int i = 0; i < etud.N_Note; i++) {
				etud.Moy += etud.Note[i];
			}
			etud.Moy = (float)etud.Moy / (float)etud.N_Note;
			cout << "M: " << setprecision(4) << etud.Moy;
		}
		cout << endl;
	};
};

class Etudiant : public Personne {
	string An_Scol;
	int Note[3];
	int N_Note;
	float Moy;

	friend class Direction;
	friend bool tri_etud(const Etudiant& a, const Etudiant& b);

public:
	static int i;

	using SAISIE = int;

	Etudiant() : Personne(++i) {};

	void saisie(int n) {
		Personne::saisie();
		
		getline(cin, An_Scol, ' ');

		string tmp;
		N_Note = n;

		for (int i = 0; i < N_Note-1; i++) {
			getline(cin, tmp, ' ');
			Note[i] = stoi(tmp);
		}
		cin >> Note[N_Note-1];
		cin.ignore();
	}

	void afficher() { Personne::afficher(); cout << left << setw(6) << An_Scol; for (int i = 0; i < N_Note; i++) cout << left << setw(4) << Note[i]; }

	// Retourne le nom de la classe pour la saisie
	static string nomclasse() { return "étudiant"; }

	static SAISIE parametre_saisie() {
		int mat = 0;
		do {
			cout << "Nombre de matière (<= 3) : ";
			cin >> mat; cin.ignore();
		} while (mat <= 0 || mat > 3);

		return mat;
	}
};

int Prof::i = 0;
int Etudiant::i = 0;


template <class C>
void saisie(C& conteneur) {
	// récupération du type stocké dans le conteneur, évite de devoir
	// passer un 2e variable en paramètre
	using A = typename C::value_type;

	// Le type "SAISIE" stocké sur la classe,
	// permet de saisir virtuellement n'importe en quoi avant la saisie de chaque élémement
	// 1x avant les itérations
	using SAISIE = typename A::SAISIE;

	int n;

	cout << "Nombre de " << A::nomclasse() << " : ";
	cin >> n; cin.ignore();

	SAISIE p = A::parametre_saisie();
	
	while (n-- > 0) {
		// A a;
		// a.saisie();
		// conteneur.insert(conteneur.end(), a);
		
		conteneur.emplace(conteneur.end())->saisie(p);
 	}
	cout << endl;
}

template <class C>
void afficher(C& conteneur) {
	using V = typename C::value_type;

	cout << "Affichage de " << V::nomclasse() << endl;

	for (auto &v : conteneur) {
		cout << endl; v.afficher();
	}
	cout << endl;
}

template <class C>
bool recherche(C& conteneur, string recherche) {
	using T = typename C::value_type;

	auto et = find(conteneur.begin(), conteneur.end(), recherche);

	if (et != conteneur.end()) {
		cout << recherche << " existe. Il s'agit d'un " << T::nomclasse();
		return true;
	}

	return false;
}

// a < b
bool tri_etud(const Etudiant& a, const Etudiant& b) {
	if (a.An_Scol.length() != 3 || b.An_Scol.length() != 3) {
		return a.Moy < b.Moy;
	}

	if (a.An_Scol[0] != b.An_Scol[0]) {
		return a.An_Scol[0] < b.An_Scol[0];
	}
	else if (a.An_Scol[2] != b.An_Scol[2]) {
		return a.An_Scol[2] < b.An_Scol[2];
	}
	else {
		return a.Moy < b.Moy;
	}
}


int menu() {
	int c;
	cout << endl;
	cout<< " 1. Saisir N Etudiants et afficher" << endl
		<< " 2. Saisir M Profs et affichage" << endl
		<< " 3. Doyen membre de Direction Calcule la moyenne de chaque étudiant et affichage" << endl
		<< " 4. Chercher selon le NOM si un Etud ou Prof existe et affichage" << endl
		<< " 5. Trier selon le Nom(Etud ou Prof) par ordre croissant et affichage" << endl
		<< " 6. Afficher la liste des étudiants par année scolaire et par ordre décroissant de leur moyenne" << endl
		<< " 7. Quitter" << endl;
	cout << "Choix :";
	cin >> c; cin.ignore();

	return c;
}

int main() {
	int c;
	string r;

	list<Etudiant> etudiants;
	vector<Prof> profs;
	Direction doyen;

	setlocale(LC_ALL, "");


	while ((c = menu()) != 7) {
		switch (c) {
		case 1:
			saisie(etudiants);
			afficher(etudiants);
			
			break;
		case 2:
			saisie(profs);
			afficher(profs);

			break;
		case 3:
			doyen.saisie();
			cout << "Affichage doyen" << endl; doyen.afficher(); cout << endl;
			doyen.moyennes(etudiants);

			break;

		case 4:
			cout << "Entrer un Nom : " << endl;
			getline(cin, r);

			recherche(etudiants, r) || recherche(profs, r) || (cout << r << " n'existe pas !! ");
			break;

		case 5:
			etudiants.sort();
			afficher(etudiants);

			sort(profs.begin(), profs.end());
			afficher(profs);
			

			break;
		case 6:
			etudiants.sort(tri_etud);
			afficher(etudiants);
			break;
		}
	}

	return 0;
}