/*
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.listes;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import fr.cnrs.liris.drim.grt.modele.Terme;
import fr.cnrs.liris.drim.grt.ihm.GestionnaireErreurs;

/**
 *
 * @author Sam
 */
public class Frequences implements Liste {
    private final static Frequences instance = new Frequences();
    
    public static Frequences getInstance() {
        return instance;
    }
    
    private Map<Cle, Integer> nbOccurrences;
    private Map<Terme, Set<String>> docsParTerme;
    private Map<String, Set<Terme>> termesParDoc;
    
    private Frequences() {
        nbOccurrences = new HashMap<>();
        docsParTerme = new HashMap<>();
        termesParDoc = new HashMap<>();
    }
    
    public void addDocument(String document, Map<Terme, Integer> occurrences) {
        calculeListes();
        if(!hasDoc(document)) {
            for(Terme t: occurrences.keySet()) {
                nbOccurrences.put(new Cle(t, document), occurrences.get(t));
                if(!docsParTerme.containsKey(t)) {
                    docsParTerme.put(t, new HashSet<String>());
                }
                docsParTerme.get(t).add(document);
                if(!termesParDoc.containsKey(document)) {
                    termesParDoc.put(document, new HashSet<Terme>());
                }
                termesParDoc.get(document).add(t);
            }
        }
    }
    
    public int getNbOccurrencesTotal(Terme t) {
        calculeListes();
        int nb = 0;
        for(String doc: docsParTerme.get(t)) {
            nb += getNbOccurrences(t, doc);
        }
        return nb;
    }
    
    public Terme[] getTermes() {
        calculeListes();
        Terme[] res = new Terme[docsParTerme.keySet().size()];
        docsParTerme.keySet().toArray(res);
        return res;
    }
    
    public String[] getDocuments() {
        calculeListes();
        String[] res = new String[termesParDoc.keySet().size()];
        termesParDoc.keySet().toArray(res);
        return res;
    }
    
    public int getNbOccurrences(Terme t, String doc) {
        calculeListes();
        return nbOccurrences.get(new Cle(t, doc));
    }
    
    public int getNbDocuments(Terme t) {
        calculeListes();
        return docsParTerme.get(t).size();
    }
    
    private boolean hasDoc(String document) {
        calculeListes();
        return termesParDoc.containsKey(document);
    }
    
    private void calculeListes() {
        if(nbOccurrences.isEmpty()) {
            // chargement du fichier
            //System.err.println("Lecture de la liste des fréquences");
            File fichier = new File("frequences.txt");
            if(fichier.exists()) {
                try {
                    BufferedReader lecteur = new BufferedReader(new InputStreamReader(new FileInputStream(fichier), "UTF-8"));
                    while(lecteur.ready()) {
                        String[] ligne = lecteur.readLine().trim().split("\t");
                        Terme t = Terme.cree(ligne[0]);
                        String document = ligne[1];
                        nbOccurrences.put(new Cle(t, document), Integer.parseInt(ligne[2]));
                        if(!docsParTerme.containsKey(t)) {
                            docsParTerme.put(t, new HashSet<String>());
                        }
                        docsParTerme.get(t).add(document);
                        if(!termesParDoc.containsKey(document)) {
                            termesParDoc.put(document, new HashSet<Terme>());
                        }
                        termesParDoc.get(document).add(t);
                    }
                } catch (IOException ex) {
                    GestionnaireErreurs.getInstance().affiche("Erreur d'application", 
                            "<html><p>Le fichier frequences.txt est présent mais illisible.</p>"
                            + "<ul><li>Choix 1 : veuillez corriger la situation si vous savez comment ;</li>"
                            + "<li>Choix 2 : veuillez le supprimer, et recréer la liste des fréquences.</li></ul>"
                            + "<p>Pour information, l'erreur est</p>"
                            + "<p>"+ex.getClass()+" : "+ex.getMessage()+"</p></html>");
                } catch(NumberFormatException nfe) {
                    GestionnaireErreurs.getInstance().affiche("Erreur d'application", 
                            "<html><p>Le fichier frequences.txt contient une valeur non entière en colonne 2 ou 3.</p>"
                            + "<ul><li>Choix 1 : veuillez corriger la situation si vous savez comment ;</li>"
                            + "<li>Choix 2 : veuillez supprimer complètement le fichier, et recréer la liste des fréquences.</li></ul>"
                            + "<p>Pour information, l'erreur est</p>"
                            + "<p>"+nfe.getClass()+" : "+nfe.getMessage()+"</p></html>");
                }
            } else {
                // pas de fichier ? Pas grave, on en crée un vide
                try {
                    fichier.createNewFile();
                } catch (IOException ex) {
                    GestionnaireErreurs.getInstance().affiche("Erreur d'application", 
                            "<html><p>Nous n'avons pas trouvé le fichier frequences.txt qui contient la liste des fréquences.</p>"
                            + "<p>Nous avons essayé de le créer, sans succès.</p>"
                            + "<p>Nous vous recommandons de nous permettre d'écrire un tel fichier avant de continuer.</p>"
                            + "<p>Pour information, l'erreur est</p>"
                            + "<p>"+ex.getClass()+" : "+ex.getMessage()+"</p></html>");
                }
            }
            //System.err.println("Fin de lecture de la liste des fréquences");
        }
    }
    
    @Override
    public void archive() {
        File fichier = new File("frequences.txt");
        if(!(fichier.exists())) {
            // pas de fichier ? Pas grave, on en crée un vide
            try {
                fichier.createNewFile();
            } catch (IOException ex) {
                GestionnaireErreurs.getInstance().affiche("Erreur d'application", 
                            "<html><p>Nous n'avons pas trouvé le fichier frequences.txt qui contient la liste des termes.</p>"
                            + "<p>Nous avons essayé de le créer, sans succès.</p>"
                            + "<p>Nous vous recommandons de nous permettre d'écrire un tel fichier avant de continuer.</p>"
                            + "<p>Pour information, l'erreur est</p>"
                            + "<p>"+ex.getClass()+" : "+ex.getMessage()+"</p></html>");
            }
        }
        try {
            BufferedWriter lecteur = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fichier), "UTF-8"));
            for(Cle cle: nbOccurrences.keySet()) {
                lecteur.write(cle.terme.getExpression() + "\t" + cle.document + "\t" + nbOccurrences.get(cle) + "\r\n");
            }
            lecteur.flush();
        } catch (IOException ex) {
            GestionnaireErreurs.getInstance().affiche("Erreur d'application", 
                            "<html><p>Le fichier frequences.txt est présent mais nous en pouvons pas l'alimenter.</p>"
                            + "<p>Veuillez corriger la situation si vous savez comment.</p>"
                            + "<p>Pour information, l'erreur est</p>"
                            + "<p>"+ex.getClass()+" : "+ex.getMessage()+"</p></html>");
        }
    }
    
    private class Cle {
        Terme terme;
        String document;
        
        private Cle(Terme t, String doc) {
            terme = t;
            document = doc;
        }
        
        @Override
        public boolean equals(Object o) {
            if(!(o instanceof Cle)) return false;
            Cle c = (Cle)o;
            return (c.terme == null ? terme == null : c.terme == terme) && 
                    (c.document == null ? document == null : c.document.equals(document));
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 47 * hash + Objects.hashCode(this.terme);
            hash = 47 * hash + Objects.hashCode(this.document);
            return hash;
        }
    }
}
