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

using namespace std;

class Biere {
	string nom;
	string type;
	float deg_alcool;
	float prix;
	string region;

public:
	void saisie(ifstream& stream);
	// Saisie (mais depuis le clavier)
	void saisie();
	void affichage();

	float get_deg_alcool() {
		return deg_alcool;
	}
	float get_prix() {
		return prix;
	}

	string get_nom() {
		return nom;
	}
};


// Ici la fonction lecture n'est pas une template
// car il doit ajouter au bon conteneur en fonction du type de bière
// On aurait pu aussi imaginer une template, avec un "filtre", qui n'ajoute que 
// dans le conteneur passé en paramètre si le type == filtre.
// Mais cela aurait nécessité 2lectures du fichier (ou une autre fonction)
void lecture_fichier(list<Biere>& belges, vector<Biere>& etrangeres, const string fname) {
	ifstream file(fname);

	if (!file.is_open()) {
		cout << "Erreur lors de l'ouverture du fichier" << endl;
		return;
	}

	while (!file.eof()) {
		string type;
		Biere b;
		
		getline(file,type, ';');
		b.saisie(file);
		
		if (type == "Belge") {
			belges.push_back(b);
		}
		else {
			etrangeres.push_back(b);
		}
	}
}


// La version template
// En soit le 2e paramètre (T) n'est pas vraiment nécessaire parce que toujours = "Biere"
// j'ai aucune idée de si il faut le mettre ou pas c'est un peu coup de poker avec
// Benjelloun, il peut te dire fallait pas le mettre pour la raison que j'ai cité ci-dessus
// mais aussi te dire mais il fallait le mettre (si on envisage l'inhéritance d'une classe bière)
template <class C, class T>
void lecture_fichier_filtre(C& conteneur, const string type, const string fname) {
	ifstream file(fname);

	if (!file.is_open()) {
		cout << "Erreur lors de l'ouverture du fichier" << endl;
		return;
	}

	while (!file.eof()) {
		string t;
		
		getline(file, t, ';');
		if (t != type) {
			file.ignore(200, '\n'); // passer la ligne, count=200 s'arrête dès que '\n'.
			continue;
		}

		T b;
		b.saisie(file);

		conteneur.push_back(b);
	}
}

template <class C>
void affichage(C& c) {
	for (auto& a : c) {
		a.affichage();
	}
}

string saisie_type_biere() {
	string t;
	cout << "Belge ou pas (O/N) :";
	cin >> t; cin.ignore();
	return t == "O" ? "Belge" : "Etranger";
}

// Pareil ici pour le type T = "Biere"
// j'aime beacoup la syntaxe
// using T = typename C::value_type
// qui évite de devoir passer T explicitement 
// mais pas sur que les assistants connaissent tous/ benjelloun apprécie
template <class C, class T>
void ajout(C& conteneur) {
	T b;
	b.saisie();
	conteneur.insert(conteneur.begin(), b);
}

void saisie_seuil(int& s) {
	cout << "Seuil : ";
	cin >> s;
}

template <class C>
void erase_seuil_alcool(C& cont, float seuil) {
	// au lieu d'itérer on aurait aussi pu utiliser remove_if (<algorithm>)
	// mais à lors il aurait fallut passer le "seuil" en paramètre avec l'équivalent 
	// d'un "higher order function" ou d'un lambda
	// mais pas vu au cours donc on fait comme ça.

	auto it = cont.begin();
	for (; it != cont.end(); /* il ne faut pas incrémenter l'itérateur à chaque fois, si on supprime on skip l'élement */) {
		// it-> est équivalent à une déréférence (*it)
		if (it->get_deg_alcool() >= seuil) {
			it = cont.erase(it);
		}
		else {
			it++;
		}
	}
}

// T= tjr bière en soit
template <class T>
bool comparer_prix(T& a, T& b) {
	return a.get_prix() < b.get_prix();
}

template <class C>
void plus_cher(C& cont) {
	// pour obtenir les bières LES + chers on peut soit 
	// itérer pour récupérer le max() en prix
	// puis réitéré et n'afficher que les bières ou 
	// prix == max
	// ou sort le conteneur O(N logN) puis prendre les N premier
	// tant que N == prixPrecedent.

	// max_element marchera pas pour PLUSIEURS bières :(
	auto it = max_element(cont.begin(), cont.end(), comparer_prix<Biere>);

	if (it == cont.end()) {
		cout << "Aucune bière dans le conteneur";
		return;
	}

	// équivalent à (*it).get_prix();
	float prixMax = it->get_prix();

	for (auto& b : cont) {
		if (b.get_prix() == prixMax) {
			// b.affichage();
			cout << b.get_nom() << " au prix de " << setprecision(4) << b.get_prix() << " euros" << endl;
		}
	}
	
}

int menu() {
	int choix;
	cout << "1. Stocker les bières Belge(s) dans une List et les bières Etrangere(s) dans un Vector + Afficher" << endl
		<< "2. Ajouter au début une bière à List ou au Vector des bières selon si la bière est Belge ou pas" << endl
		<< "3. Supprimer les bières dont le degré d'alcool est supérieure à un seuil donné par l’utilisateur" << endl
		<< "4. Afficher les bières les plus chères du List et du Vector" << endl;

	cout << "Choix:" << endl;
	cin >> choix;
	return choix;
}

int main() {
	list<Biere> belges;
	vector<Biere> etrangeres;
	
	int seuil;

	setlocale(LC_ALL, "");
	
	while (true) {
		switch (menu()) {
		case 1:
			// Ici, aucune idée de la bonne version
			// Aussi un coup de poker avec Benjelloun.
			// (lecture du fichier 2x vs pas de template+2conteneurs en paramètres)

			// Avec la première fonction lecture
			// lecture_fichier(belges, etrangeres, "Biere2.txt");
			
			// Avec la seconde utilisant des templates et un filtre
			lecture_fichier_filtre<list<Biere>, Biere>(belges, "Belge", "Biere2.txt");
			lecture_fichier_filtre<vector<Biere>, Biere>(etrangeres, "Etrangere", "Biere2.txt");

			cout << "Bières Belges:" << endl;
			affichage(belges);
			cout << "Bières étrangères" << endl;
			affichage(etrangeres);
			break;

		case 2:
			if (saisie_type_biere() == "Belge") {
				ajout<list<Biere>, Biere>(belges);
				cout << "Bières Belges:" << endl;
				affichage(belges);
			}
			else {
				ajout<vector<Biere>, Biere>(etrangeres);
				cout << "Bières étrangères" << endl;
				affichage(etrangeres);
			}
			break;

		case 3:
			saisie_seuil(seuil);

			erase_seuil_alcool(belges, seuil);
			erase_seuil_alcool(etrangeres, seuil);

			cout << "Bières Belges:" << endl;
			affichage(belges);
			cout << "Bières étrangères" << endl;
			affichage(etrangeres);

			break;

		case 4:
			cout << "Les bières belges la plus chères sont" << endl;
			plus_cher(belges);
			cout << "Les bières étrangères la plus chères sont" << endl;
			plus_cher(etrangeres);

			break;
		default:
			return 0; // sortie
		}
	}
}

void Biere::saisie(ifstream& s) {
	string tmp;
	getline(s, nom, ';');
	getline(s, type, ';');

	
	getline(s, tmp, ';');

	// POUR UNE RAISON QUE J'IGNORE complétement, sur VisualStudio 2022
	// Avec windows en FRANCAIS, le stof lis les nombre avec des VIRGULES et pas des points
	// Il est nécessaire de retirer ceci en fonction de la plateforme ou du compilateur
	// Voir même de la langue de Windows (on pourra le faire mais je le fais pas)
	replace(tmp.begin(), tmp.end(), '.', ',');


	deg_alcool = stof(tmp); // fonction stof: String TO Float

	getline(s, tmp, ';');
	
	// Pareil ici :/
	replace(tmp.begin(), tmp.end(), '.', ',');

	prix = stof(tmp);

	getline(s, region, '\n');
}

void Biere::affichage() {
	cout << nom << "; " << type << "; " << deg_alcool << "; " << prix<<"; "<<region<<endl;
}

void Biere::saisie() {
	cout << "Nom de la bière : ";
	getline(cin, nom);
	cout << "Un type : ";
	getline(cin, type);
	cout << "Un degré d'alcool : ";
	cin >> deg_alcool;
	cout << "Prix : ";
	cin >> prix; cin.ignore();
	cout << "Région/Pays : ";
	getline(cin, region);
}