
#pragma once
#ifndef saisieMR_deja
#define saisieMR_deja
#include <iostream>
#include <limits>
#include <string>
#include <fstream>

using namespace std;

//const int maxInt = numeric_limits<int>::max();

#define bulle "\n______  _______________________________\n      |/\n"
#define lapin " (\\_/)\n( . .)\nc(\")(\")\n"

/*       (\_/)   fonction
        ( . . ) /
###########################################*/

//equivalent à system(clear)
void viderEcran()
{
    for (int i=0;i<25;i++)
        cout << '\n';
}
//il semble que cette fonction pose probleme



/*       (\_/)   classe
        ( . . ) /
###########################################*/
//petit exemple pour surcharger les streams << >>
//class strstr
//{
//  string buffer;
//public:
//    friend ostream& operator<< (ostream &out, string &ligne)
//  {
//      out << ligne;
//      return out;
//  }
//    friend istream& operator>> (istream &in, string &ligne)
//  {
//      in >> ligne;
//      return in;
//  }
//  strstr & operator << (string & ligne)
//  {
//      return *this;
//  }
//  strstr & operator >> (string & ligne)
//  {
//      return *this;
//  }
//};

/*  (\_/) fichier
___( . . )_/________________
classe de lecture de fichier
elle a un fonctionnement tres
pres de celui du pseudocode
standar utilisé.
elle permet d'utiliser les
operateur stream tout en ayant
une détection d'érreure
__________________________*/

class lecture
{
    ifstream fichier;           //l'objet du fichier
public:
    lecture (string nomFichier);    //constructeur avec ouverture de fichier
    bool ouvrire(string nomFichier);    //ouverture de fichier
    void fermer();      //fermeture du fichier
    operator bool ();   //surcharge d'operateur bool, usage bool test = lecture; retourne ok
    //lecture d'un Entier dans un fichier
    lecture & operator >> (int & entier);       //surcharge de l'operateur stream avec entier
    //lecture d'un nombre decimal dans un fichier
    lecture & operator >> (float & naturel);        //surcharge de l'operateur stream avec decimal
    //lecture d'une lettre dans un fichier
    lecture & operator >> (char & lettre);      //surcharge de l'operateur stream avec une lettre
    //lecture d'une string dans un fichier
    lecture & operator >> (string & mot);       //surcharge de l'operateur stream avec une string
    //lecture d'un tableau de charactere dans un fichier
    lecture & operator >> (char mot[]);       //surcharge de l'operateur stream avec une string
};

//constructeur avec ouverture de fichier
lecture::lecture (string nomFichier)
{
    ouvrire(nomFichier);
}

//ouverture de fichier
bool lecture::ouvrire(string nomFichier)
{
    fichier.open(nomFichier.c_str());       //ouverture du fichier
    if (fichier.fail())     //si le fichier est absent
    {
        cout << "le fichier \"" << nomFichier << "\" est absent\n";
        return false;
    }
    if (fichier.peek() == ifstream::traits_type::eof()) //si le fichier est vide
    {
        cout << "le fichier \"" << nomFichier << "\" est vide\n";
        return false;
    }
    return true;
}

//fermeture du fichier
void lecture::fermer()
{
    fichier.close();
}

lecture::operator bool ()    //surcharge d'operateur bool, usage bool test = lecture; retourne ok
{
    while (fichier.peek() == 32 || fichier.peek() == 10)    //verification de fin de fichier
        fichier.get();                                      //en sautant les ESPACEs et les NEWLINEs
    return fichier.good() || !fichier.eof();
}

//lecture d'un Entier dans un fichier
lecture & lecture::operator >> (int & entier)        //surcharge de l'operateur stream avec entier
{
    fichier >> entier;
    if (fichier.fail())                     //dans le cas d'un echec
    {
        entier = 0;
        cout << "erreur " << fichier.cur << " attendu entier" << endl;
    }
    return *this;                       //il doit se retourner lui-meme pour la prochaine variable
}

//lecture d'un nombre decimal dans un fichier
lecture & lecture::operator >> (float & naturel)     //surcharge de l'operateur stream avec decimal
{
    fichier >> naturel;
    if (fichier.fail())                     //dans le cas d'un echec
    {
        naturel = 0;
        cout << "erreur " << fichier.cur << " attendu naturel" << endl;
    }
    return *this;
}

//lecture d'une lettre dans un fichier
lecture & lecture::operator >> (char & lettre)       //surcharge de l'operateur stream avec une lettre
{
    lettre = fichier.get();
    if (fichier.fail())                     //dans le cas d'un echec
    {
        lettre = 0;
        cout << "erreur " << fichier.cur << " attendu charactere" << endl;
    }
    return *this;
}

//lecture d'une string dans un fichier
lecture & lecture::operator >> (string & mot)        //surcharge de l'operateur stream avec une string
{
    fichier >> mot;
    if (fichier.fail())                     //dans le cas d'un echec
    {
        mot = "";
        cout << "erreur " << fichier.cur << " attendu serie de charactere" << endl;
    }
    return *this;
}

//lecture d'une string dans un fichier
lecture & lecture::operator >> (char mot[])        //surcharge de l'operateur stream avec une string
{
    fichier >> mot;
    if (fichier.fail())                     //dans le cas d'un echec
    {
        cout << "erreur " << fichier.cur << " attendu serie de charactere" << endl;
    }
    return *this;
}

/*  (\_/) saisie
___( . . )_/________________
la saisie du clavier avec
validation et message.
Ici je fait usage de:
-Classe avec inheritance
-Template et polymorphisme
-surcharge d'operateur
on peut facilement les comparers
au foncteur a quelque différence
pres.
__________________________*/

/*  classe de base
 _______________________
 c'est la class mere de
 toute celle qui suivront
 dans la saisie
 _____________________*/
template <class T>
class entree{
public:
    string msg;             //message utilisateur
    entree() {}             //constructeur
    entree(string message);     //constructeur , donné lui le message à la saisie
    entree& operator >> (T& sortie);        //surcharge d'operateur stream sortie
    T operator () ();                   //surcharge d'operateur fonctionnel
    void vider();            //vider le buffer de cin
    virtual T fonction();    //polymorphisme, elle est là que pour être surchargé
};

template <class T>
entree<T>::entree(string message)  //constructeur , donné lui le message à la saisie
{
    msg = message;
}

template <class T>
entree<T>& entree<T>::operator >> (T& sortie)     //surcharge d'operateur stream sortie
{
    sortie = fonction();            //permet l'usage telque saisie >> variable
    return *this;
}

template <class T>
T entree<T>::operator () ()                    //surcharge d'operateur fonctionnel
{
    return fonction();              //permet l'usage telque variable = saisie
}

template <class T>
void entree<T>::vider()            //vider le buffer de cin
{
    cin.clear();                    //vider les flags
    cin.seekg(0,ios_base::end);     //traverse tout le buffer
    if (!cin.fail())
        cin.ignore(numeric_limits<int>::max());         //vider le buffer
    else
        cin.clear();                //vider les flags
}

template <class T>
T entree<T>::fonction()    //polymorphisme, elle est là que pour être surchargé
{
    return NULL;
}

/*  foncteur de saisie d'un nombre float ou int
 _______________________
 avec les templates, je
 peux avoir un seul code
 pour plusieurs type de
 donnée semblable.
 A chaque initialisation
 je doit fournire le <type>
 et le compilateur creera
 une classe en fonction
 _____________________*/

template <class nombre>                         //avec les templates, une seule classe et toute sorte de type
class saisie: public entree <nombre>                //elle dérive d'entree et lui donne le meme type
{
public:
    saisie(string message):entree<nombre>(message) { }   //constructeur , appel le constructeur parent
    nombre fonction();                          //polymorphisme, ici que l'objet change
};

template <class nombre>
nombre saisie<nombre>::fonction()                           //polymorphisme, ici que l'objet change
{
    nombre entier;              //variable temporaire du <type> demandé pour la saisie
    do
    {
        entree<nombre>::vider();                //vider le buffer
        cout << entree<nombre>::msg;    //afficher le message
        cin >> entier;          //saisie d'un nombre
    }while (cin.fail());        //tanque ce n'est pas reussie
    return entier;
}

/*  foncteur de saisie d'entier positif
 _______________________*/
template <>                                                 //template spécialisé unsigned int, une exception avec un type spécifique
class saisie<unsigned int>:public entree<unsigned int>      //meme classe mais avec des modifications
{
public:
    saisie(string message):entree<unsigned int>(message) { }                //constructeur, appel le constructeur parent
    unsigned int fonction();                                    //polymorphisme
};

unsigned int saisie<unsigned int>::fonction()                                 //polymorphisme
{
    int entierPositif;                  //entier temporaire pour la saisie
    do
    {
        entree<unsigned int>::vider();                        //vider le buffer
        cout << entree<unsigned int>::msg;            //afficher le message
        cin >> entierPositif;           //saisie d'un nombre
    }while (cin.fail() ||
            (signed int)entierPositif <= 0);    //tanque ce n'est pas reussie, ou que le nombre n'est pas positif
    return (unsigned int)entierPositif; //retyper en nombre non signer
}
/*  foncteur de saisie de string
 _______________________*/
template <>                                 //template spécialisé unsigned int, une exception avec un type spécifique
class saisie<string>:public entree<string>  //cette classe prend en charge les strings
{
public:
    saisie(string message):entree<string>(message) { }  //constructeur
    string fonction();                          //polymorphisme
};

string saisie<string>::fonction()                           //polymorphisme
{
    string ligne;                           //string temporaire pour la saisie
    do
    {
        vider();                        //vider le buffer
        cout << msg;            //afficher le message
        getline(cin,ligne);             //prendre une ligne dans le cin
    }while(ligne == "");                //recommencer si elle est vide
    return ligne;                       //retourner la ligne
}
/*  foncteur de saisie d'un seul charactere
 _______________________*/
template <>                             //template specialisé
class saisie<char>:public entree<char>  //avec le type char
{
    string validation;                  //variable interne privé, liste des charactere autorisé
    bool dansLaListe(char choix);       //vérification de la présence d'un charactere dans la liste (non sensible a la case)
public:
    saisie(string message):entree<char>(message) { }            //constructeur
    saisie(string message, string liste);               //constructeur avec validation
    char fonction();
};

bool saisie<char>::dansLaListe(char choix)        //vérification de la présence d'un charactere dans la liste (non sensible a la case)
{
    for (unsigned int i=0;i<validation.length();i++)        //chaque charactere de la liste
        if (toupper(choix) == toupper(validation[i]))       //s'il sont pareil en majuscule
            return true;                                    //alors c'est vrai
    return false;
}

saisie<char>::saisie(string message, string liste)                //constructeur avec validation
{
    validation = liste;         //stocker la liste
    msg = message;              //et le message
}
char saisie<char>::fonction()
{
    char choix;
    if (validation == "")       //s'il n'y a pas de validation, ne prendre qu'un charactere
    {
        vider();                //vider le buffer
        cout << msg;    //afficher le message
        choix = cin.get();      //saisire le charactere
    }
    else                        //autrement
    {
        do
        {
            vider();                //vider buffer
            cout << msg;    //afficher message
            choix = cin.get();      //saisir le charactere
        }while(!dansLaListe(choix)); //tanque la verification avec la liste authorisé ne donne pas vrai
    }
    return choix;
}

/*  foncteur de saisie d'un seul charactere sur deux avec retour booleen
 _______________________*/
template <>                             //template spécialisé
class saisie<bool>:public entree<bool>  //avec les données booleen
{
    char vrai,          //le charactere qui donnera vrai
         faux;          //le charactere qui donnera faux
public:
    saisie(string message,char v, char f);  //constructeur, prend les deux charactere vrai et faux
    bool fonction();                    //polymorphise
};

saisie<bool>::saisie(string message,char v, char f):entree<bool>(message) //constructeur, prend les deux charactere vrai et faux
{
    vrai=toupper(v);
    faux=toupper(f);
}

bool saisie<bool>::fonction()                 //polymorphise
{
    char choix;                 //character temporaire pour la saisie
    do
    {
        vider();                //vider le buffer à chaque fois
        cout << msg;    //afficher le message
        choix = toupper(cin.get());     //saisire
    }while(choix != vrai && choix != faux); //tanque ce n'est pas un des deux caracteres demandé
    return choix == vrai;       //retourne la valeure booleen du choix
}
/*  foncteur de saisie de clavier NULL
 _______________________*/
template <>                             //template spécialisé
class saisie<void>:public entree<bool>  //si la saisie est de type void, elle héritera d'entree en type bool
{
public:
    saisie(string message):entree<bool>(message) { }    //constructeur
    bool fonction ();                           //polymorphisme
};

bool saisie<void>::fonction ()                            //polymorphisme
{
    cout << msg;                //afficher le message
    vider();                    //vider le buffer
    cin.ignore(numeric_limits<int>::max(),'\n');    //attendre que l'utilisateur appuis sur entrer
    return true;                //retourner true
}


/*       (\_/)   fini
        ( . . ) /
###########################################*/
#endif
