package fr.univParisDiderot.phys;

import javax.swing.JOptionPane;
import java.awt.HeadlessException;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.io.*;
import java.util.StringTokenizer;

/**
 * La classe <code>ES</code> contient quelques méthodes pour faire des
 * <b>E</b>ntrées/<b>S</b>orties simplement.
 *
 * <P>Voici un exemple d'utilisation de <code>ES</code>:
 * <PRE>
 * import static fr.univParisDiderot.phys.ES.*;
 *
 * public class Test {
 *   public static void main(String args[]) {
 *       println("coucou");
 *       println(3.14);
 *       messageDialog("oh c'est joli!");
 *       float x = stringToFloat(inputDialog("valeur du paramètre x?",1.41));
 *       println("paramètre x = "+x);
 *       println("Maintenant dans le terminal: valeur du paramètre y?");
 *       float y = stringToFloat(readToken());
 *       println("paramètre y = "+y);
 *       exit();
 *   }
 * }
 * </PRE>
 *
 * @author Adrian Daerr (adrian.daerr'at'univ-paris-diderot.fr),
 * inspiré par les fonctionnalités de <a
 * href="http://www.liafa.jussieu.fr/~yunes/deug/">la classe DEUG de
 * Jean-Baptiste Yunès</a> (Jean-Baptiste.Yunes'at'liafa.jussieu.fr)
 * @version 0.1
 */
public class ES {

    // on en a besoin d'une (même invisible) comme parent de nos dialogues...
    static Frame frame=new Frame();

    // et on ouvre l'entrée standard pour d'éventuelles lectures
    static LineNumberReader input = new LineNumberReader(new InputStreamReader(System.in));
    static StringTokenizer tokenizer = null;

    /** Lit une ligne entière de texte sur l'entrée standard. Renvoie
     * la chaîne de caractères que l'utilisateur à tapée avant
     * d'appuyer sur la touche &lt;Entrée&gt;.
     */
    public static String readLine() {
        String s;
        try {
            s = input.readLine();
        } catch (IOException e) {
            yaErreur("Erreur lors de la lecture de l'entrée standard.",e);
            return null;
        }
        return s;
    }

    /** Lit un mot de texte sur l'entrée standard. Plutôt que de lire
     * ligne par ligne, cette méthode lit mot par mot, s'arrêtant à
     * chaque espace ou retour à la ligne rencontré. Pratique si on
     * veut lire la valeur d'une variable par exemple:<br />
     *
     * <code>float y = stringToFloat(readToken());</code>
     */
    public static String readToken() {
        while (tokenizer==null || !tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(readLine());
        }
        return tokenizer.nextToken();
    }

    /** Affiche un objet sur la sortie standard. Un objet peut être un
     * entier, un flottant, etc.
     */
    public static void print(Object o) {
        System.out.println(o);
    }

    /** Affiche un objet sur la sortie standard, et revient à la
     * ligne. Tout est un objet en Java (un entier, un flottant,
     * etc.), donc cette fonction peut afficher n'importe quoi.
     */
    public static void println(Object o) {
        System.out.println(o);
    }

    /** Affiche du texte sur la sortie standard.
     */
    public static void print(String s) {
        System.out.println(s);
    }

    /** Affiche du texte sur la sortie standard, et revient à la ligne.
     */
    public static void println(String s) {
        System.out.println(s);
    }

    /** Affiche du texte dans une boîte de dialogue. L'utilisateur
     * doit fermer le dialogue (en cliquant sur un bouton, ou en
     * fermant la fenêtre) avant que la fonction retourne et que le
     * programme puise continuer.
     *
     * @see javax.swing.JOptionPane#showMessageDialog
     * @see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/dialog.html">How to make Dialogs (Java tutorial)</a>
     * @param s Le message à afficher dans le dialogue
     */
    public static void messageDialog(String s) {
        try {
            JOptionPane.showMessageDialog(null, s);
        } catch (HeadlessException e) {
            yaErreur("Erreur: pas d'écran graphique ?\n"+
                     "Le message suivant n'a pas pu être affiché "+
                     "dans un dialogue:\n"+s,e);
        }
        return;
    }

    /** Demande de l'information à l'utilisateur dans une boîte de
     * dialogue. Le texte tapé par l'utilisateur est renvoyé sous
     * forme de String, on peut utiliser une autre fonction de cette
     * bibliothèque pour le convertir. Pour une conversion en float
     * par exemple:<br />
     *
     * <code>float x = stringToFloat(inputDialog("valeur du paramètre
     * x?",3.14));</code>
     *
     * @see javax.swing.JOptionPane
     * @see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/dialog.html">How to make Dialogs (Java tutorial)</a>
     * @param question Question affichée dans le dialogue
     * @param valeurInit valeur par défaut du texte
     * @return Texte entré par l'utilisateur
     */
    public static String inputDialog(String question, Object valeurInit) {
        String s;
        try {
            s = JOptionPane.showInputDialog(null, question, valeurInit);
        } catch (HeadlessException e) {
            yaErreur("Erreur: pas d'écran graphique ?\n"+
                     "La question suivante n'a pas pu être posée "+
                     "dans un dialogue:\n"+question,e);
            return null;
        }
        return s;
    }

    /** Affiche une fenêtre de dialogue avec la question donnée en
     * argument, et deux boutons OUI/NON de réponse. Si l'utilisateur
     * choisi de répondre oui, cette méthode renvoie la valeur vrai.
     * Si l'utilisateur choisi non ou qu'une erreur survient, la
     * méthode renvoie la valeur faux.
     *
     * @return true pour le bouton YES, false pour le bouton NO
     */
    public static boolean confirmDialog(String question) {
        boolean reponse=false;
        try {
            int n = JOptionPane.showConfirmDialog(null, question,
                                                    "Question...",
                                                    JOptionPane.YES_NO_OPTION);
            reponse = (n==JOptionPane.YES_OPTION);
        } catch (HeadlessException e) {
            yaErreur("Erreur: pas d'écran graphique ?\n"+
                     "La question suivante n'a pas pu être posée "+
                     "dans un dialogue:\n"+question,e);
        }
        return reponse;
    }

    /** Convertit du texte en un nombre flottant de type double. Tente
     * d'interpréter la chaîne de caractères donnée en entrée comme un
     * nombre à virgule flottante de double précision. Si la
     * conversion échoue, un message d'erreur est affiché sur la
     * sortie erreur standard.
     */
    public static double stringToDouble(String s) {
        double res;
        try {
            res=Double.parseDouble(s);
        } catch (Exception e) {// il y a eu un problème
            yaErreur("Erreur à la conversion en (double) de: "+s,e);
            return (double)0;
        }
        return res;
    }

    /** Convertit du texte en un nombre flottant de type float. Tente
     * d'interpréter la chaîne de caractères donnée en entrée comme un
     * nombre à virgule flottante de simple précision. Si la
     * conversion échoue, un message d'erreur est affiché sur la
     * sortie erreur standard.
     */
    public static float stringToFloat(String s) {
        float res;
        try {
            res=Float.parseFloat(s);
        } catch (Exception e) {// il y a eu un problème
            yaErreur("Erreur à la conversion en (float) de: "+s,e);
            return (float)0;
        }
        return res;
    }

    /** Convertit du texte en un entier de type int. Tente
     * d'interpréter la chaîne de caractères donnée en entrée comme un
     * nombre entier. Si la conversion échoue, un message d'erreur est
     * affiché sur la sortie erreur standard.
     */
    public static int stringToInt(String s) {
        int res;
        try {
            res=Integer.parseInt(s);
        } catch (Exception e) {// il y a eu un problème
            yaErreur("Erreur à la conversion en (int) de: "+s,e);
            return (int)0;
        }
        return res;
    }

    /** Convertit du texte en un entier de type long. Tente
     * d'interpréter la chaîne de caractères donnée en entrée comme un
     * nombre entier. Si la conversion échoue, un message d'erreur est
     * affiché sur la sortie erreur standard.
     */
    public static long stringToLong(String s) {
        long res;
        try {
            res=Long.parseLong(s);
        } catch (Exception e) {// il y a eu un problème
            yaErreur("Erreur à la conversion en (long) de: "+s,e);
            return (long)0;
        }
        return res;
    }

    /** Devient vrai si une opération (lecture de l'entrée,
     * conversion) échoue. Cette variable n'est pas lisible pas
     * directement, il faut utiliser la méthode erreurSurvenue().
     */
    static boolean erreur = false;

    /** méthode appelée en interne quand une erreur est survenue
     */
    static void yaErreur(String message, Exception e) {
        System.err.println(message);
        e.printStackTrace();
        if (GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadless()) {
            System.exit(1);
        } else {
            int reponse=JOptionPane.NO_OPTION;
            try {
                reponse = JOptionPane.showConfirmDialog(frame, message+"\n\nContinuer le programme ?", "Erreur survenue", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE);
            } catch (HeadlessException he) {/*should never reach this*/}
            if (reponse==JOptionPane.NO_OPTION) System.exit(1);
        }
        erreur = true;
        return;
    }

    /** <p>Renvoie 'vrai' si une erreur (lecture p ex dans readln(),
     * conversion) est survenue depuis le dernier appel à cette
     * méthode (ou depuis le début, si c'est le premier appel).
     * </p>
     *
     * <p>Exemple d'utilisation:</p>
     * <pre>
     *   float y;
     *   do {// redemander tant qu'il y a des erreurs
     *       println("valeur du paramètre y?");
     *       y = stringToFloat(readToken());
     *   } while (erreurSurvenue());
     *   println("paramètre y = "+y);
     * </pre>
     */
    public static boolean erreurSurvenue() {
        boolean reponse = erreur;
        erreur = false;// remettre à zéro
        return reponse;
    }

    /** Termine l'execution du programme.
     */
    public static void exit() {
        try { input.close(); }
        catch (IOException e) {e.printStackTrace();}
        System.exit(0);
    }
}