/*
Copyright 2012-2014 Samuel Gesche

This file is part of the Greek Reuse Toolkit.

The Greek Reuse Toolkit is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

The Greek Reuse Toolkit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the Greek Reuse Toolkit.  If not, see <http://www.gnu.org/licenses/>.
*/

package fr.cnrs.liris.drim.grt.modele;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
import fr.cnrs.liris.drim.grt.modele.exceptions.CoordonneeIntrouvableException;

/**
 *
 * @author Sam
 */
public class OrdreStrict implements Comparator<Coordonnee> {
    /**
     * L'ordre déroulant la liste des entiers positifs dans la numérotation arabe.
     */
    public static final String NUMERIQUE_ARABE = "numériqueArabe";
    /**
     * L'ordre des livres bibliques dans la Septante.
     */
    public static final String LIVRES_LXX = "livre(LXX)";
    /**
     * L'ordre des livres bibliques dans le Nouveau Testament Grec.
     */
    public static final String LIVRES_NTG = "livre(NTG)";
    /**
     * L'ordre des livres bibliques.
     */
    public static final String LIVRES_BIB = "livre(Bible)";
    /**
     * L'ordre des livres de Philon.
     */
    public static final String LIVRES_PHI = "livre(Philon)";
    /**
     * L'ordre des mots dans un passage (un ordre numérique arabe).
     */
    public static final String MOTS = "mot";
    /**
     * L'ordre des versets dans un chapitre (un ordre numérique arabe).
     */
    public static final String VERSETS = "verset";
    /**
     * L'ordre des chapitres dans un livre (un ordre numérique arabe).
     */
    public static final String CHAPITRES = "chapitre";
    /**
     * L'ordre des paragraphes dans un passage (un ordre numérique arabe).
     */
    public static final String PARAGRAPHES = "paragraphe";
    /**
     * L'ordre des lignes dans un passage (un ordre numérique arabe).
     */
    public static final String LIGNES = "ligne";
    
    private static final ArrayList<String> listeLivresLXXGR = new ArrayList<>();
    private static final ArrayList<String> listeLivresLXXEN = new ArrayList<>();
    //private static final ArrayList<String> listeLivresLXXFR = new ArrayList<>();
    private static final ArrayList<String> listeLivresNTGGR = new ArrayList<>();
    private static final ArrayList<String> listeLivresNTGEN = new ArrayList<>();
    //private static final ArrayList<String> listeLivresNTGFR = new ArrayList<>();
    private static final ArrayList<String> listeLivresGR = new ArrayList<>();
    private static final ArrayList<String> listeLivresEN = new ArrayList<>();
    //private static final ArrayList<String> listeLivresFR = new ArrayList<>();
    private static final ArrayList<String> listeLivresPhi = new ArrayList<>();
    
    static {
        listeLivresLXXGR.add("Γένεσις");
        listeLivresLXXGR.add("Ἔξοδος");
        listeLivresLXXGR.add("Λευϊτικόν");
        listeLivresLXXGR.add("Ἀριθμοί");
        listeLivresLXXGR.add("Δευτερονόμιον");
        listeLivresLXXGR.add("Ἰησοῦς (B)");
        listeLivresLXXGR.add("Ἰησοῦς (A)");
        listeLivresLXXGR.add("Κριταί (A)");
        listeLivresLXXGR.add("Κριταί (B)");
        listeLivresLXXGR.add("Ρούθ");
        listeLivresLXXGR.add("Βασιλειῶν αʹ");
        listeLivresLXXGR.add("Βασιλειῶν βʹ");
        listeLivresLXXGR.add("Βασιλειῶν γʹ");
        listeLivresLXXGR.add("Βασιλειῶν δʹ");
        listeLivresLXXGR.add("Παραλειπομένων αʹ");
        listeLivresLXXGR.add("Παραλειπομένων βʹ");
        listeLivresLXXGR.add("Ἔσδρας αʹ");
        listeLivresLXXGR.add("Ἔσδρας βʹ");
        listeLivresLXXGR.add("Ἐσθήρ");
        listeLivresLXXGR.add("Ἰουδίθ");
        listeLivresLXXGR.add("Τωβίτ (ΒΑ)");
        listeLivresLXXGR.add("Μακκαβαίων αʹ");
        listeLivresLXXGR.add("Μακκαβαίων βʹ");
        listeLivresLXXGR.add("Μακκαβαίων γʹ");
        listeLivresLXXGR.add("Μακκαβαίων δʹ");
        listeLivresLXXGR.add("Ψαλμοί");
        listeLivresLXXGR.add("Ὠδαί");
        listeLivresLXXGR.add("Παροιμίαι");
        listeLivresLXXGR.add("Ἐκκλησιαστής");
        listeLivresLXXGR.add("Ἆισμα");
        listeLivresLXXGR.add("Ἰώβ");
        listeLivresLXXGR.add("Σοφία Σαλωμῶνος");
        listeLivresLXXGR.add("Σοφία Σιράχ");
        listeLivresLXXGR.add("Ψαλμοί Σολομῶντος");
        listeLivresLXXGR.add("Ὠσηέ");
        listeLivresLXXGR.add("Ἀμώς");
        listeLivresLXXGR.add("Μιχαίας");
        listeLivresLXXGR.add("Ἰωήλ");
        listeLivresLXXGR.add("Ἀβδιού");
        listeLivresLXXGR.add("Ἰωνᾶς");
        listeLivresLXXGR.add("Ναούμ");
        listeLivresLXXGR.add("Ἀμβακούμ");
        listeLivresLXXGR.add("Σοφονίας");
        listeLivresLXXGR.add("Ἀγγαῖος");
        listeLivresLXXGR.add("Ζαχαρίας");
        listeLivresLXXGR.add("Μαλαχίας");
        listeLivresLXXGR.add("Ἠσαΐας");
        listeLivresLXXGR.add("Ἰερεμίας");
        listeLivresLXXGR.add("Βαρούχ");
        listeLivresLXXGR.add("Θρῆνοι");
        listeLivresLXXGR.add("Ἐπιστολὴ Ἰερεμίου");
        listeLivresLXXGR.add("Ἰεζεκιήλ");
        listeLivresLXXGR.add("Σουσαννα");
        listeLivresLXXGR.add("Σουσαννα (Θ)");
        listeLivresLXXGR.add("Δανιήλ");
        listeLivresLXXGR.add("Δανιήλ (Θ)");
        listeLivresLXXGR.add("Βηλ καὶ δράκων");
        listeLivresLXXGR.add("Βηλ καὶ δράκων (Θ)");
        
        listeLivresLXXEN.add("Gen");
        listeLivresLXXEN.add("Exo");
        listeLivresLXXEN.add("Lev");
        listeLivresLXXEN.add("Num");
        listeLivresLXXEN.add("Deu");
        listeLivresLXXEN.add("Jsa");
        listeLivresLXXEN.add("Jos");
        listeLivresLXXEN.add("Jda");
        listeLivresLXXEN.add("Jdg");
        listeLivresLXXEN.add("Rut");
        listeLivresLXXEN.add("1Sa");
        listeLivresLXXEN.add("2Sa");
        listeLivresLXXEN.add("1Ki");
        listeLivresLXXEN.add("2Ki");
        listeLivresLXXEN.add("1Ch");
        listeLivresLXXEN.add("2Ch");
        listeLivresLXXEN.add("1Es");
        // Attention : Ezr et Neh sont tous deux contenus dans Ἔσδρας βʹ
        listeLivresLXXEN.add("Ezr");
        listeLivresLXXEN.add("Neh");
        listeLivresLXXEN.add("Est");
        listeLivresLXXEN.add("Jdt");
        listeLivresLXXEN.add("Tob");
        // Attention : Tbs n'est pas dans l'index grec
        listeLivresLXXEN.add("Tbs");
        listeLivresLXXEN.add("1Ma");
        listeLivresLXXEN.add("2Ma");
        listeLivresLXXEN.add("3Ma");
        listeLivresLXXEN.add("4Ma");
        listeLivresLXXEN.add("Psa");
        listeLivresLXXEN.add("Ode");
        listeLivresLXXEN.add("Pro");
        listeLivresLXXEN.add("Ecc");
        listeLivresLXXEN.add("Sol");
        listeLivresLXXEN.add("Job");
        listeLivresLXXEN.add("Wis");
        // Attention : Sip et Sir sont tous deux contenus dans Σοφία Σιράχ
        listeLivresLXXEN.add("Sip");
        listeLivresLXXEN.add("Sir");
        listeLivresLXXEN.add("Pss");
        listeLivresLXXEN.add("Hos");
        listeLivresLXXEN.add("Amo");
        listeLivresLXXEN.add("Mic");
        listeLivresLXXEN.add("Joe");
        listeLivresLXXEN.add("Oba");
        listeLivresLXXEN.add("Jon");
        listeLivresLXXEN.add("Nah");
        listeLivresLXXEN.add("Hab");
        listeLivresLXXEN.add("Zep");
        listeLivresLXXEN.add("Hag");
        listeLivresLXXEN.add("Zec");
        listeLivresLXXEN.add("Mal");
        listeLivresLXXEN.add("Isa");
        listeLivresLXXEN.add("Jer");
        listeLivresLXXEN.add("Bar");
        listeLivresLXXEN.add("Lam");
        listeLivresLXXEN.add("Epj");
        listeLivresLXXEN.add("Eze");
        listeLivresLXXEN.add("Sus");
        listeLivresLXXEN.add("Sut");
        listeLivresLXXEN.add("Dan");
        listeLivresLXXEN.add("Dat");
        listeLivresLXXEN.add("Bel");
        listeLivresLXXEN.add("Bet");
        
        /*listeLivresLXXFR.add("Genèse");
        listeLivresLXXFR.add("Exode");
        listeLivresLXXFR.add("Lévitique");
        listeLivresLXXFR.add("Nombres");
        listeLivresLXXFR.add("Deutéronome");
        listeLivresLXXFR.add("Josué a");
        listeLivresLXXFR.add("Josué");
        listeLivresLXXFR.add("Juges a");
        listeLivresLXXFR.add("Juges");
        listeLivresLXXFR.add("Ruth");
        listeLivresLXXFR.add("1 Samuel");
        listeLivresLXXFR.add("2 Samuel");
        listeLivresLXXFR.add("1 Rois");
        listeLivresLXXFR.add("2 Rois");
        listeLivresLXXFR.add("1 Chroniques");
        listeLivresLXXFR.add("2 Chroniques");
        listeLivresLXXFR.add("1Esd");
        // Attention : Ezr et Neh sont tous deux contenus dans Ἔσδρας βʹ
        listeLivresLXXFR.add("Esd");
        listeLivresLXXFR.add("Ne");
        listeLivresLXXFR.add("Est");
        listeLivresLXXFR.add("Jdt");
        listeLivresLXXFR.add("Tob");
        // Attention : Tbs n'est pas dans l'index grec
        listeLivresLXXFR.add("Tbs");
        listeLivresLXXFR.add("1Ma");
        listeLivresLXXFR.add("2Ma");
        listeLivresLXXFR.add("3Ma");
        listeLivresLXXFR.add("4Ma");
        listeLivresLXXFR.add("Psa");
        listeLivresLXXFR.add("Ode");
        listeLivresLXXFR.add("Pro");
        listeLivresLXXFR.add("Ecc");
        listeLivresLXXFR.add("Sol");
        listeLivresLXXFR.add("Job");
        listeLivresLXXFR.add("Wis");
        // Attention : Sip et Sir sont tous deux contenus dans Σοφία Σιράχ
        listeLivresLXXFR.add("Sip");
        listeLivresLXXFR.add("Sir");
        listeLivresLXXFR.add("Pss");
        listeLivresLXXFR.add("Hos");
        listeLivresLXXFR.add("Amo");
        listeLivresLXXFR.add("Mic");
        listeLivresLXXFR.add("Joe");
        listeLivresLXXFR.add("Oba");
        listeLivresLXXFR.add("Jon");
        listeLivresLXXFR.add("Nah");
        listeLivresLXXFR.add("Hab");
        listeLivresLXXFR.add("Zep");
        listeLivresLXXFR.add("Hag");
        listeLivresLXXFR.add("Zec");
        listeLivresLXXFR.add("Mal");
        listeLivresLXXFR.add("Isa");
        listeLivresLXXFR.add("Jer");
        listeLivresLXXFR.add("Bar");
        listeLivresLXXFR.add("Lam");
        listeLivresLXXFR.add("Epj");
        listeLivresLXXFR.add("Eze");
        listeLivresLXXFR.add("Sus");
        listeLivresLXXFR.add("Sut");
        listeLivresLXXFR.add("Dan");
        listeLivresLXXFR.add("Dat");
        listeLivresLXXFR.add("Bel");
        listeLivresLXXFR.add("Bet");*/
        
        listeLivresNTGGR.add("Κατὰ Μαθθαῖον");
        listeLivresNTGGR.add("Κατὰ Μᾶρκον");
        listeLivresNTGGR.add("Κατὰ Λουκᾶν");
        listeLivresNTGGR.add("Κατὰ Ἰωάννην");
        listeLivresNTGGR.add("Πράξεις Ἀποστόλων");
        listeLivresNTGGR.add("Πρὸς Ῥωμαίους");
        listeLivresNTGGR.add("Πρὸς Κορινθίους α’");
        listeLivresNTGGR.add("Πρὸς Κορινθίους β’");
        listeLivresNTGGR.add("Πρὸς Γαλάτας");
        listeLivresNTGGR.add("Πρὸς Ἐφεσίους");
        listeLivresNTGGR.add("Πρὸς Φιλιππησίους");
        listeLivresNTGGR.add("Πρὸς Κολοσσαεῖς");
        listeLivresNTGGR.add("Πρὸς Θεσσαλονικεῖς α’");
        listeLivresNTGGR.add("Πρὸς Θεσσαλονικεῖς β’");
        listeLivresNTGGR.add("Πρὸς Τιμόθεον α’");
        listeLivresNTGGR.add("Πρὸς Τιμόθεον β’");
        listeLivresNTGGR.add("Πρὸς Τίτον");
        listeLivresNTGGR.add("Πρὸς Φιλήμονα");
        listeLivresNTGGR.add("Πρὸς Ἑβραίους");
        listeLivresNTGGR.add("Ἰακώβου");
        listeLivresNTGGR.add("Πέτρου α’");
        listeLivresNTGGR.add("Πέτρου β’");
        listeLivresNTGGR.add("Ἰωάννου α’");
        listeLivresNTGGR.add("Ἰωάννου β’");
        listeLivresNTGGR.add("Ἰωάννου γ’");
        listeLivresNTGGR.add("Ἰούδα");
        listeLivresNTGGR.add("Ἀποκάλυψις Ἰωάννου");
        
        listeLivresNTGEN.add("Mat");
        listeLivresNTGEN.add("Mar");
        listeLivresNTGEN.add("Luk");
        listeLivresNTGEN.add("Joh");
        listeLivresNTGEN.add("Act");
        listeLivresNTGEN.add("Rom");
        listeLivresNTGEN.add("1Co");
        listeLivresNTGEN.add("2Co");
        listeLivresNTGEN.add("Gal");
        listeLivresNTGEN.add("Eph");
        listeLivresNTGEN.add("Phi");
        listeLivresNTGEN.add("Col");
        listeLivresNTGEN.add("1Th");
        listeLivresNTGEN.add("2Th");
        listeLivresNTGEN.add("1Ti");
        listeLivresNTGEN.add("2Ti");
        listeLivresNTGEN.add("Tit");
        listeLivresNTGEN.add("Phm");
        listeLivresNTGEN.add("Heb");
        listeLivresNTGEN.add("Jam");
        listeLivresNTGEN.add("1Pe");
        listeLivresNTGEN.add("2Pe");
        listeLivresNTGEN.add("1Jo");
        listeLivresNTGEN.add("2Jo");
        listeLivresNTGEN.add("3Jo");
        listeLivresNTGEN.add("Jud");
        listeLivresNTGEN.add("Rev");
        
        listeLivresGR.addAll(listeLivresLXXGR);
        listeLivresGR.addAll(listeLivresNTGGR);
        listeLivresEN.addAll(listeLivresLXXEN);
        listeLivresEN.addAll(listeLivresNTGEN);
        
        
        listeLivresPhi.add("Abr");
        listeLivresPhi.add("Aet");
        listeLivresPhi.add("Agr");
        listeLivresPhi.add("Che");
        listeLivresPhi.add("Lin");
        listeLivresPhi.add("Cng");
        listeLivresPhi.add("Dec");
        listeLivresPhi.add("Qei");
        listeLivresPhi.add("Ebr");
        listeLivresPhi.add("Fug");
        listeLivresPhi.add("Gig");
        listeLivresPhi.add("Ios");
        listeLivresPhi.add("Mig");
        listeLivresPhi.add("Mut");
        listeLivresPhi.add("Opi");
        listeLivresPhi.add("Pla");
        listeLivresPhi.add("Pos");
        listeLivresPhi.add("Pep");
        listeLivresPhi.add("Prv");
        listeLivresPhi.add("Sac");
        listeLivresPhi.add("Sob");
        listeLivresPhi.add("Som");
        listeLivresPhi.add("Spe");
        listeLivresPhi.add("Vir");
        listeLivresPhi.add("Cnt");
        listeLivresPhi.add("Mos");
        listeLivresPhi.add("Hyp");
        listeLivresPhi.add("Fla");
        listeLivresPhi.add("Gai");
        listeLivresPhi.add("Leg");
        listeLivresPhi.add("Qex");
        listeLivresPhi.add("Qge");
        listeLivresPhi.add("Her");
        listeLivresPhi.add("Pot");
        listeLivresPhi.add("Imm");
        listeLivresPhi.add("Prb");
        listeLivresPhi.add("Qgi");
        listeLivresPhi.add("Qgp");

        
        /*try {
        new File("log.xls").createNewFile();
        BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("log.xls")), "UTF-8"));
        for(String s: listeLivresLXXGR) {
            fw.write(s+"\r\n");
        }
        fw.write("\r\n\r\n");
        for(String s: listeLivresLXXEN) {
            fw.write(s+"\r\n");
        }
        fw.write("\r\n\r\n");
        for(String s: listeLivresNTGGR) {
            fw.write(s+"\r\n");
        }
        fw.write("\r\n\r\n");
        for(String s: listeLivresNTGEN) {
            fw.write(s+"\r\n");
        }
        fw.flush();
        } catch(Exception e) {}*/
    }
    
    private String code = NUMERIQUE_ARABE;
    
    private OrdreStrict(String code) {
        this.code = code;
    }
    
    /**
     * Donne l'ordre strict demandé. Voir les attributs constants pour la liste 
     * des ordres possibles.
     * @param type le type d'ordre souhaité
     * @return l'ordre demandé
     */
    public static OrdreStrict getInstance(String type) {
        return new OrdreStrict(type);
    }
    
    /**
     * Dit si oui ou non l'ordre contient un nom de coordonnée demandé
     * @param nom un nom de coordonnée à retrouver
     * @return oui ou non
     * @throws CoordonneeIntrouvableException si l'ordre est inconnu (et non si 
     * la coordonnée est introuvable, auquel cas la réponse est false.
     */
    public boolean contient(String nom) throws CoordonneeIntrouvableException {
        if(nom.equals(Coordonnee.FIN)) {
            return true;
        }
        boolean resultat = false;
        switch(code) {
            case NUMERIQUE_ARABE:
            case MOTS:
            case VERSETS:
            case CHAPITRES:
            case PARAGRAPHES:
            case LIGNES:
                try {
                    int i = Integer.parseInt(nom);
                    resultat = true;
                } catch(NumberFormatException nfe) {
                    // n'est pas un nombre en chiffres arabes, donc le résultat
                    // reste faux
                }
                break;
            case LIVRES_LXX:
                resultat = listeLivresLXXGR.contains(nom) || 
                        listeLivresLXXEN.contains(nom);
                break;
            case LIVRES_NTG:
                resultat = listeLivresNTGGR.contains(nom) || 
                        listeLivresNTGEN.contains(nom);
                break;
            case LIVRES_BIB:
                resultat = listeLivresGR.contains(nom) || 
                        listeLivresEN.contains(nom);
                break;
            case LIVRES_PHI:
                resultat = listeLivresPhi.contains(nom);
                break;
            default:
                throw new CoordonneeIntrouvableException("Ordre inconnu (" + code + ") : impossible de trouver si " + nom + " en fait partie.");
        }
        return resultat;
    }
    
    /**
     * Dit si oui ou non l'ordre contient une coordonnée du nom demandé
     * @param coordonnee une coordonnée à retrouver
     * @return oui ou non
     * @throws CoordonneeIntrouvableException si l'ordre est inconnu (et non si 
     * la coordonnée est introuvable, auquel cas la réponse est false.
     */
    public boolean contient(Coordonnee coordonnee) throws CoordonneeIntrouvableException {
        return contient(coordonnee.getExpression());
    }
    
    /**
     * Dit si oui ou non l'ordre contient une coordonnée après celle demandée.
     * 
     * Caveat : pour une coordonnée numérique, il y a toujours un suivant.
     * 
     * @param coordonnee une coordonnee à tester
     * @return oui ou non
     */
    public boolean hasSuivant(Coordonnee coordonnee) {
        //System.out.println("     --- test de suite : " + coordonnee + " ---");
        try {
            getSuivant(coordonnee);
            return true;
        } catch(CoordonneeIntrouvableException e) {
            return false;
        }
    }
    
    /**
     * Dit si oui ou non l'ordre contient une coordonnée avant celle demandée.
     * @param coordonnee une coordonnee à tester
     * @return oui ou non
     */
    public boolean hasPrecedent(Coordonnee coordonnee) {
        try {
            getPrecedent(coordonnee);
            return true;
        } catch(CoordonneeIntrouvableException e) {
            return false;
        }
    }
    
    /**
     * Donne la coordonnée suivante dans cet ordre.
     * @param coordonnee la coordonnée dont on veut la suivante
     * @return la coordonnée suivante
     * @throws CoordonneeIntrouvableException si la coordonnée n'est pas dans l'ordre, ou si c'est la dernière.
     */
    public Coordonnee getSuivant(Coordonnee coordonnee) throws CoordonneeIntrouvableException {
        if(coordonnee.getExpression().equals(Coordonnee.FIN)) {
            throw new CoordonneeIntrouvableException("Dernier livre atteint.");
        }
        String resultat = "";
        int index;
        switch(code) {
            case NUMERIQUE_ARABE:
            case MOTS:
            case VERSETS:
            case CHAPITRES:
            case PARAGRAPHES:
            case LIGNES:
                try {
                    index = Integer.parseInt(coordonnee.getExpression());
                } catch(NumberFormatException nfe) {
                    throw new CoordonneeIntrouvableException(coordonnee.getExpression() + " n'est pas un nombre entier ; impossible de trouver le suivant dans l'ordre numérique.");
                }
                resultat = "" + (index + 1);
                break;
            case LIVRES_LXX:
                if(listeLivresLXXGR.contains(coordonnee.getExpression())) {
                    index = listeLivresLXXGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Septante (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresLXXGR.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresLXXGR.get(index+1);
                } else {
                    index = listeLivresLXXEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Septante (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresLXXEN.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresLXXEN.get(index+1);
                }
                break;
            case LIVRES_NTG:
                if(listeLivresNTGGR.contains(coordonnee.getExpression())) {
                    index = listeLivresNTGGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans le Nouveau Testament (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresNTGGR.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresNTGGR.get(index+1);
                } else {
                    index = listeLivresNTGEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans le Nouveau Testament (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresNTGEN.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresNTGEN.get(index+1);
                }
                break;
            case LIVRES_BIB:
                if(listeLivresGR.contains(coordonnee.getExpression())) {
                    index = listeLivresGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Bible (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresGR.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresGR.get(index+1);
                } else {
                    index = listeLivresEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Bible (" + coordonnee.getExpression()+").");
                    } else if(index == listeLivresEN.size()-1) {
                        throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                    }
                    resultat = listeLivresEN.get(index+1);
                }
                break;
            case LIVRES_PHI:
                index = listeLivresPhi.indexOf(coordonnee.getExpression());
                if(index == -1) {
                    throw new CoordonneeIntrouvableException("Livre inconnu dans Philon (" + coordonnee.getExpression()+").");
                } else if(index == listeLivresPhi.size()-1) {
                    throw new CoordonneeIntrouvableException("Dernier livre atteint.");
                }
                resultat = listeLivresPhi.get(index+1);
                break;
            default:
                throw new CoordonneeIntrouvableException("Ordre inconnu (" + code + ") : impossible de trouver le suivant.");
        }
        return new Coordonnee(this, resultat);
    }
    
    /**
     * Donne la coordonnée précédente dans cet ordre.
     * @param coordonnee la coordonnée dont on veut la précédente
     * @return la coordonnée précédente
     * @throws CoordonneeIntrouvableException si la coordonnée n'est pas dans l'ordre, ou si c'est la première.
     */
    public Coordonnee getPrecedent(Coordonnee coordonnee) throws CoordonneeIntrouvableException {
        if(coordonnee.getExpression().equals(Coordonnee.FIN)) {
            // Oh le vilain qui ne gère pas !
            throw new CoordonneeIntrouvableException("Impossible de récupérer l'avant-dernière coordonnée pour l'instant.");
        }
        String resultat = "";
        int index;
        switch(code) {
            case NUMERIQUE_ARABE:
            case MOTS:
            case VERSETS:
            case CHAPITRES:
            case PARAGRAPHES:
            case LIGNES:
                try {
                    index = Integer.parseInt(coordonnee.getExpression());
                } catch(NumberFormatException nfe) {
                    throw new CoordonneeIntrouvableException(coordonnee.getExpression() + " n'est pas un nombre entier ; impossible de trouver le précédent dans l'ordre numérique.");
                }
                resultat = "" + (index - 1);
                break;
            case LIVRES_LXX:
                if(listeLivresLXXGR.contains(coordonnee.getExpression())) {
                    index = listeLivresLXXGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Septante (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresLXXGR.get(index-1);
                } else {
                    index = listeLivresLXXEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Septante (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresLXXEN.get(index-1);
                }
                break;
            case LIVRES_NTG:
                if(listeLivresNTGGR.contains(coordonnee.getExpression())) {
                    index = listeLivresNTGGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans le Nouveau Testament (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresNTGGR.get(index-1);
                } else {
                    index = listeLivresNTGEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans le Nouveau Testament (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresNTGEN.get(index-1);
                }  
                break;
            case LIVRES_BIB:
                if(listeLivresGR.contains(coordonnee.getExpression())) {
                    index = listeLivresGR.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Bible (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresGR.get(index-1);
                } else {
                    index = listeLivresEN.indexOf(coordonnee.getExpression());
                    if(index == -1) {
                        throw new CoordonneeIntrouvableException("Livre inconnu dans la Bible (" + coordonnee.getExpression()+").");
                    } else if(index == 0) {
                        throw new CoordonneeIntrouvableException("Premier livre atteint.");
                    }
                    resultat = listeLivresEN.get(index-1);
                }  
                break;
            case LIVRES_PHI:
                index = listeLivresPhi.indexOf(coordonnee.getExpression());
                if(index == -1) {
                    throw new CoordonneeIntrouvableException("Livre inconnu dans Philon (" + coordonnee.getExpression()+").");
                } else if(index == 0) {
                    throw new CoordonneeIntrouvableException("Premier livre atteint.");
                }
                resultat = listeLivresPhi.get(index-1);
                break;
            default:
                throw new CoordonneeIntrouvableException("Ordre inconnu (" + code + ") : impossible de trouver le précédent.");
        }
        return new Coordonnee(this, resultat);
    }
    
    /**
     * Donne la distance dans cet ordre entre deux coordonnées. La distance peut 
     * être négative.
     * @param index1 une première coordonnée
     * @param index2 une seconde coordonnée
     * @return la distance entre index1 et index2
     * @throws CoordonneeIntrouvableException si l'une des coordonnées n'est pas dans cet ordre
     */
    public int getDistance(Coordonnee index1, Coordonnee index2) throws CoordonneeIntrouvableException {
        if(!contient(index1) || !index1.getSysteme().equals(this)) {
            throw new CoordonneeIntrouvableException(index1 + " n'est pas dans " + code);
        }
        if(!contient(index2) || !index2.getSysteme().equals(this)) {
            throw new CoordonneeIntrouvableException(index2 + " n'est pas dans " + code);
        }
        if(index1.getExpression().equals(Coordonnee.FIN) || index2.getExpression().equals(Coordonnee.FIN)) {
            throw new CoordonneeIntrouvableException("La 'distance jusqu'à la fin' n'est pas définie hors contexte.");
        }
        int resultat = 0;
        switch(code) {
            case NUMERIQUE_ARABE:
            case MOTS:
            case VERSETS:
            case CHAPITRES:
            case PARAGRAPHES:
            case LIGNES:
                resultat = Integer.parseInt(index2.getExpression()) - Integer.parseInt(index1.getExpression());
                break;
            case LIVRES_LXX: // rajouter le traitement si les deux sont dans des listes différentes
                if(listeLivresLXXGR.contains(index1.getExpression())) {
                    resultat = listeLivresLXXGR.indexOf(index2.getExpression()) - 
                            listeLivresLXXGR.indexOf(index1.getExpression());
                } else {
                    resultat = listeLivresLXXEN.indexOf(index2.getExpression()) - 
                            listeLivresLXXEN.indexOf(index1.getExpression());
                }
                break;
            case LIVRES_NTG: // rajouter le traitement si les deux sont dans des listes différentes
                if(listeLivresNTGGR.contains(index1.getExpression())) {
                    resultat = listeLivresNTGGR.indexOf(index2.getExpression()) - 
                            listeLivresNTGGR.indexOf(index1.getExpression());
                } else {
                    resultat = listeLivresNTGEN.indexOf(index2.getExpression()) - 
                            listeLivresNTGEN.indexOf(index1.getExpression());
                }
                break;
            case LIVRES_BIB: // rajouter le traitement si les deux sont dans des listes différentes
                if(listeLivresGR.contains(index1.getExpression())) {
                    resultat = listeLivresGR.indexOf(index2.getExpression()) - 
                            listeLivresGR.indexOf(index1.getExpression());
                } else {
                    resultat = listeLivresEN.indexOf(index2.getExpression()) - 
                            listeLivresEN.indexOf(index1.getExpression());
                }
                break;
            case LIVRES_PHI:
                resultat = listeLivresPhi.indexOf(index2.getExpression()) - 
                            listeLivresPhi.indexOf(index1.getExpression());
                break;
            default:
                throw new CoordonneeIntrouvableException("Ordre inconnu (" + code + ") : impossible de calculer une distance.");
        }
        return resultat;
    }
    
    /**
     * Ordonne la liste fournie selon cet OrdreStrict.
     * @param listeDesordonnee une liste supposée non ordonnée
     * @return la même liste, ordonnée
     */
    public Coordonnee[] getListeOrdonnee(Collection<Coordonnee> listeDesordonnee) {
        Coordonnee[] resultat = new Coordonnee[listeDesordonnee.size()];
        listeDesordonnee.toArray(resultat);
        Arrays.sort(resultat, this);
        return resultat;
    }
    
    /**
     * Déclare si oui ou non cet ordre est identique à celui proposé.
     * @param autreOrdre l'ordre proposé pour le test
     * @return oui ou non
     */
    @Override
    public boolean equals(Object autreOrdre) {
        if(!(autreOrdre instanceof OrdreStrict)) {
            return false;
        }
        return ((OrdreStrict)autreOrdre).code.equals(code);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 13 * hash + Objects.hashCode(this.code);
        return hash;
    }
    
    /**
     * Donne une version textuelle de cet ordre.
     * @return ladite version textuelle
     */
    @Override
    public String toString() {
        return code;
    }

    @Override
    public int compare(Coordonnee o1, Coordonnee o2) {
            return getDistance(o2, o1);
    }
}
