//inclusion de la bibliotheque standard fstream,flux associés à des fichiers
#include <fstream>//pour ifstream et ofstream
//bibli standard associée à des flux d'entrés sortis standard
#include <iostream>//pour cin et cout
//bibli contenant la définition de la classe string
#include <string>// pour string
//bibli contenant la def de la classe vector (tableau dynamique)
#include <vector>
//on construit une constante qui a pour nom NOMDUFICHIER et pour valeur adresse.txt
#define NOMDUFICHIER "ficEleves.txt"
//pour le setw de cout
#include <iomanip>
#include <sstream>
//on utilise l'espace de nom standard
using namespace std;
void entrer();
/**********************************************************************************************************************/

//on déclare un fonction nommée split qui renvoit un vecteur de chaines
vector <string> split(char separateur, string chaineASplitter)
{
	//cette fonction renvoie un vecteur contenant les différents elts de la chaine
	//ces elts sont séparés dans la chaine par le séparateur spécifié en paramétre
	//on déclare une variable nommée elt du type chaines de caracteres et on l'initialise à "rien du tout"
	string elt="";
	//on déclare un vecteur de chaines de caracteres nommé vectElts
	vector <string> vectElts;
	//le nombre de fois ou l'on va passer est = aux nombre de caracteres de la chaineASplitter
	for (uint no=0; no<chaineASplitter.length();no++)
	{
		//on declare un variable nommée caracExamin de type char contenant la lettre coorespondante au tour de boucle
		//de chaineASplitter
		char caracExamine=chaineASplitter[no];
		if (caracExamine==separateur)
		{
			vectElts.push_back(elt);
			elt="";
		}
		else
			elt+=caracExamine;
	}
	if(chaineASplitter!="")
		vectElts.push_back(elt);
	return vectElts;
}
/**********************************************************************************************************************/
int menu()
	//affichage du menu et saisie contôlée du choix effectué par l'utilisateur
{
	int choix;
	ifstream ifsEleves(NOMDUFICHIER);
	bool fichierAbsent=ifsEleves.fail();
	bool choixMauvais=false;
	do
	{
		system("clear");
		if(choixMauvais) cout<<"Attention, "<<choix<<" ne correspond pas à aucune des fonctionnalités de l'application."<<endl;
		      cout<<"************************* Menu *******************"<<endl;
		cout<<"1: \tAjouter un étudiant"<<endl;
		if (!fichierAbsent)
		{
			cout<<"2: \tLister les étudiants"<<endl;
			cout<<"3: \tRechercher les informations d'un étudiant"<<endl;
			ifsEleves.close();
		}
		cout<<"4: \tQuitter"<<endl;

		cout<<endl<<"**************************************************"<<endl<<"Veuillez taper votre choix: ";
		cin>>choix;
		cin.ignore();//je mange le caractère \n restant dans le clavier
		choixMauvais=(choix<=0 ||choix>4||(fichierAbsent && (choix==2 ||choix==3)));
	}
	while(choixMauvais);
	return choix;
} 
/**********************************************************************************************************************/

string saisieChaineNonVide(string message)
	//saisie contrôlée d'une chaine par l'utilisateur taille limite 2
{
	cout<<endl<<message;
	string chaineLue;
	bool ok=true;
	do
	{
		if(!ok) cout<<endl<<"La saisie de cette information est obligatoire."<<endl<<message;
		getline(cin,chaineLue);
		ok=chaineLue.length()>=2;

	}
	while(!ok);
	return chaineLue;
}
/**********************************************************************************************************************/
bool isDouble( const string & Str )
	//cette fonction renvoie vrai si la chaîne passée en paramètre est convertible en réel
{
	// créer un flux à partir de la chaîne donnée
	std::istringstream iss( Str );
	// créer un objet temporaire pour la conversion
	double tmp;
	// tenter la conversion et
	// vérifier qu'il ne reste plus rien dans la chaîne
	return ( iss >> tmp ) && ( iss.eof() );
}
/**********************************************************************************************************************/

double toDouble(const string & Str )
	//cette fonction renvoie un réel à partir d'une chaîne passée en paramètre
{
	if(isDouble(Str))
	{
		// créer un flux à partir de la chaîne donnée
		std::istringstream iss( Str );
		// créer un objet temporaire pour la conversion
		double tmp;
		// faire la conversion et renvoyer le résultat
		iss >> tmp;
		return ( tmp); 
	}
	else
		return false;
}
/**********************************************************************************************************************/
string saisieControlee(string message, vector <string> listeValeurs)
	//cette fonction renvoie une chaine saisie par l'utilisateur 
	//la chaine doit exister dans l'ensemble de valeurs listeValeurs
{
	vector<string>::iterator itValeur;
	string chaineLue;
	bool trouve;
	//boucle
	do
	{
		//j'affiche l'invite
		cout<<endl<<message;
		//je fais saisir la valeur
		getline(cin,chaineLue);
		//recherche dans le vecteur d'une valeur identique à celle saisie
		itValeur=find(listeValeurs.begin(),listeValeurs.end(),chaineLue);
		//ai-je trouvé ?
		trouve=((itValeur!=listeValeurs.end()) && (*itValeur==chaineLue));
		//si je n'ai pas trouvé j'affiche un Message d'erreur
		if(!trouve) cout<<endl<<"Information incorrecte"<<endl<<"Veuillez renouveler votre saisie: ";
	}
	while(!trouve);
	//fin de boucle qd la valeur saisie est dans la liste passée en paramètre
	return chaineLue;
}
/**********************************************************************************************************************/
string saisieReelIntervalle(string invite,double min,double max)
	//cette fonction organise la saisie par un utilisateur d'un réel compris entre min et max
{
	bool ok=true;
	string valeurLue;
	do
	{
		if(!ok) cout<<endl<<"La valeur entrée: "<<valeurLue<<" est incorrecte pour ce type d'information";
		cout<<endl<<invite;
		getline(cin,valeurLue);

		if(isDouble(valeurLue))
		{
			double valeur=toDouble(valeurLue);
			if(valeur>max||valeur<min)
			{
				ok=false	   ;
			}
			else ok=true;
		}
		else
		{
			ok=false;
		}
	}
	while(!ok);
	return valeurLue;
}
/**********************************************************************************************************************/
void ajoutEtudiant()
	//saisie des renseignements relatifs à un étudiant et ajout de cet étudiant dans le fichier
{
	string nomEleve,pnomEleve,bacEleve,tailleEleve,sexeEleve;
	//efface ecran
	system("clear");
	//titre
	cout<<"************* Ajout d'un étudiant ****************"<<endl;

	ofstream ofsEleves(NOMDUFICHIER,ios::app);
	//saisie contrôlée du nom de l'élève
	nomEleve=saisieChaineNonVide("Veuillez saisir le nom de l'élève: ");
	//saisie contrôlée du prénom
	pnomEleve=saisieChaineNonVide("Veuillez saisir le prénom de l'élève: ");
	//saisie contrôlée du bac
	vector <string> valOk;
	valOk.push_back("S");
	valOk.push_back("ES");
	valOk.push_back("L");
	valOk.push_back("MRBT");
	valOk.push_back("STTIG");
	valOk.push_back("STTCG");
	bacEleve=saisieControlee("Veuillez saisir le bac d'origine de l'élève(S,ES,L,MRBT,STTIG,STTCG): ",valOk);
	//saisie contrôlée du sexe de l'élève
	vector <string> valOkSexe;
	valOkSexe.push_back("g");
	valOkSexe.push_back("G");
	valOkSexe.push_back("f");
	valOkSexe.push_back("F");
	valOkSexe.push_back("m");
	valOkSexe.push_back("M");

	sexeEleve=saisieControlee("Veuillez saisir le sexe de l'élève (g:garçon f:fille): ",valOkSexe);
	
	//saisie contrôlée de la taille de l'élève
	tailleEleve=saisieReelIntervalle("Veuillez saisir la taille de l'élève (ex: 1.75): ",1.0,2.5);
	
	//on ajoute l'élève en tant que ligne de texte dans le fichier (séparateur: ;)
	ofsEleves<<nomEleve<<";"<<pnomEleve<<";"<<bacEleve<<";"<<sexeEleve<<";"<<tailleEleve<<endl;
	
	//on ferme le fichier
	ofsEleves.close();

	//message
	cout<<endl<<"Message: L'élève "<<pnomEleve<<" "<<nomEleve<<" a été ajouté au fichier"<<endl;
	entrer();
}
/**********************************************************************************************************************/
void entrer()
	//cette procédure permet de faire des pauses dans le programme
{
	cout<<endl<<"... Pressez la touche \"Entrer\" pour revenir au menu...";
	string tampon;
	getline(cin,tampon);
}
/**********************************************************************************************************************/
void afficheEleves()
	//affiche tous les élèves à l'écran
{
	//j'efface l'écran
	system("clear");
	//je mets le titre
	cout<<"                   Liste des étudiants";
	int nombreDeLignes=0;
	string ligneLue,ligneALire;
	vector <string> vecEleves;
	//ouverture en mode lecture
	ifstream ifsEleves(NOMDUFICHIER);
	//si l'ouverture a été effectuée avec succés
	//comme toutes les instances de la classe ifstream elle dispose de la méthode fail 
	if(!ifsEleves.fail())
	{
		//j'affiche l'entête du tableau
		cout<<endl<<setfill('*')<<setw(20)<<left<<"|"<<setw(20)<<"|"<<setw(6)<<"|"<<setw(5)<<"|"<<setw(7)<<"|"<<"|";
		cout<<endl<<setfill(' ')<<setw(20)<<left<<"|Nom"<<setw(20)<<"|Prénom"<<setw(6)<<"|Bac"<<setw(5)<<"|Sexe"<<setw(7)<<"|Taille|";
		cout<<endl<<setfill('*')<<setw(20)<<left<<"|"<<setw(20)<<"|"<<setw(6)<<"|"<<setw(5)<<"|"<<setw(7)<<"|"<<"|"<<setfill(' ');
		getline(ifsEleves,ligneLue);												
		//boucle de lecture
		////sortie qd eof
		//tant que non condition de sortie
		while (!(ifsEleves.eof()))
		{
			vecEleves=split(';',ligneLue);

			cout<<endl<<setw(20)<<left<<"|"+vecEleves[0]<<setw(20)<<"|"+vecEleves[1]<<setw(6)<<"|"+vecEleves[2]<<setw(5)<<"|"+vecEleves[3]<<setw(7)<<"|"+vecEleves[4]<<"|";
			getline(ifsEleves,ligneLue);
			nombreDeLignes++;
		}
		//close est une méthode
		ifsEleves.close();

	}
	else cout<<endl<<"Attention: l'ouverture du fichier "<<NOMDUFICHIER<<" a échoué."<<endl;

	cout<<endl<<"Statistiques:"<<endl<<"\tIl y a "<<nombreDeLignes-1<<" étudiants dans le fichier."<<endl;
	entrer();

}
/**********************************************************************************************************************/
void rechercheEleve()
{
	string nom,ligneLue;
	vector <string> vecEleves;
	//j'efface l'écran
	system("clear");
	//je mets le titre
	cout<<"***************** Recherche d'étudiant ********************";
	cout<<endl<<"Tapez le nom de l'étudiant recherché: ";
	getline(cin,nom);
	//ouverture du fichier en mode lecture
	ifstream ifsEleves (NOMDUFICHIER);

	if(!ifsEleves.fail())
	{
		getline(ifsEleves,ligneLue);
		vecEleves=split(';',ligneLue);
		while(!(ifsEleves.eof() || vecEleves[0]==nom))
		{
			getline(ifsEleves,ligneLue);
			vecEleves=split(';',ligneLue);
		}
		//sortie qd eof ou qd trouve
		if (vecEleves[0]==nom) //si on l'a trouvé on l'affiche
		{
			cout<<endl<<"Nom: "<<vecEleves[0]<<endl<<"Prénom: "<<vecEleves[1]<<endl<<"Bac: "<<vecEleves[2]<<endl<<"Sexe: "<<vecEleves[3]<<endl<<"Taille: "<<vecEleves[4]<<endl;
		}
		else
		{
			cout<<endl<<"L'étudiant nommé "<<nom<< " est absent du fichier."<<endl;
		}
		//fermeture du fichier
		ifsEleves.close();


	}
	else
	{
		cout<<endl<<"Fichier vide ou inexistant"<<endl;

	}
	entrer();

}
/**********************************************************************************************************************/
int main()
{


	int choix;

	do{
		choix=menu();
		switch(choix)
		{
			case 1:{      ajoutEtudiant(); 
				       break;
			       }								

			case 2:{	afficheEleves();
				       break;
			       }

			case 3:{
				       rechercheEleve();
				       break;
			       }
			case 4: cout<<endl<<"Merci et à bientôt"<<endl;
				break;
			default:cout<<"Votre choix "<<choix<<" ne correspond à aucune des fonctionnalités du programme."<<endl;


		}	//fin du switch

	}
	//sortie qd choix ==4 (quitter)
	while(choix!=4);
}





