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

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import fr.cnrs.liris.drim.grt.modele.Corpus;
import fr.cnrs.liris.drim.grt.modele.Lemme;
import fr.cnrs.liris.drim.grt.modele.Passage;
import fr.cnrs.liris.drim.grt.modele.Terme;
import fr.cnrs.liris.drim.grt.proc.Texte;

/**
 *
 * @author sgesche
 */
public class Outils {
    /**
     * Indique que les termes seront utilisés tels quels
     */
    public final static int TERMES_BRUTS = 0;
    /**
     * Indique que les termes seront normalisés (plus de flexion)
     */
    public final static int TERMES_NORMALISES = 1;
    /**
     * Indique que les termes seront lemmatisés
     */
    public final static int TERMES_LEMMATISES = 2;
    
    
    
    /**
     * Donne le nombre de fois qu'un terme donné apparaît dans un document donné.
     * Les approches disponibles sont par terme (comparaison des expressions) ou 
     * par lemme (combien y a-t-il de termes qui ont au moins un lemme commun 
     * avec celui proposé).
     * @param t un terme à mesurer
     * @param d un document à explorer
     * @param traitement le traitement à appliquer aux termes
     * @return ladite mesure
     */
    public static int getFrequenceTermeDoc(Terme t, Passage d, int traitement) {
        switch(traitement) {
            case TERMES_BRUTS:
                return new Texte(d).getNbOccurrencesTerme(t);
            case TERMES_NORMALISES:
                return new Texte(d).getNbOccurrencesTermeNormalise(t);
            case TERMES_LEMMATISES:
                return new Texte(d).getNbOccurrencesLemme((Lemme)t);
            default:
                return new Texte(d).getNbOccurrencesTerme(t);
        }
    }
    
    /**
     * Donne le nombre de fois qu'un terme donné apparaît dans un corpus donné.
     * Les approches disponibles sont par terme (comparaison des expressions) ou 
     * par lemme (combien y a-t-il de termes qui ont au moins un lemme commun 
     * avec celui proposé).
     * @param t un terme à mesurer
     * @param c un document à explorer
     * @param traitement le traitement à appliquer aux termes
     * @return ladite mesure
     */
    public static int getFrequenceTerme(Terme t, Corpus c, int traitement) {
        int resultat = 0;
        for(Passage doc: c.getDocuments()) {
            resultat += getFrequenceTermeDoc(t, doc, traitement);
        }
        return resultat;
    }
    
    /**
     * Donne le nombre de document où un terme donné apparaît dans un corpus donné.
     * Les approches disponibles sont par terme (comparaison des expressions) ou 
     * par lemme (combien y a-t-il de termes qui ont au moins un lemme commun 
     * avec celui proposé).
     * @param t un terme à mesurer
     * @param d un corpus à consulter
     * @param traitement le traitement à appliquer aux termes
     * @return ladite mesure
     */
    public static int getFrequenceDocumentaire(Terme t, Corpus c, int traitement) {
        int resultat = 0;
        for(Passage doc: c.getDocuments()) {
            resultat += getFrequenceTermeDoc(t, doc, traitement)>0?1:0;
        }
        return resultat;
    }
    
    
    /**
     * Donne le TF-IDF d'un terme donné dans un corpus donné.
     * Défini comme la somme des TF-IDF du terme dans chacun des documents. 
     * Le terme peut ou non être supposé présent dans le document ou le corpus.
     * Si le terme est absent du corpus (division par 0), le résultat est 0.
     * Les approches disponibles sont par terme (comparaison des expressions) ou 
     * par lemme (combien y a-t-il de termes qui ont au moins un lemme commun 
     * avec celui proposé).
     * @param t un terme à mesurer
     * @param c un corpus à consulter
     * @param traitement le traitement à appliquer aux termes
     * @return le TF.IDF du terme dans le document et le corpus
     */
    public static double getTF_IDF(Terme t, Corpus c, int traitement) {
        int df = getFrequenceDocumentaire(t, c, traitement);
        if(df == 0) {
            return 0;
        }
        int tf = getFrequenceTerme(t, c, traitement);
        return 1.0 * tf / df;
    }
    
    public static Map<Terme, Integer> getAllTF(Corpus c, int traitement) {
        Texte[] textes = new Texte[c.getDocuments().length];
        for(int i=0; i<c.getDocuments().length; i++) {
            textes[i] = new Texte(c.getDocuments()[i]);
        }
        Map<Terme, Integer> nbOcc = new HashMap<>();
        for(Texte txt: textes) {
            Set<Terme> tt;
            switch(traitement) {
                case TERMES_BRUTS:
                    tt = txt.getTousTermes();
                    break;
                case TERMES_NORMALISES:
                    tt = txt.getTousTermesNormalises();
                    break;
                case TERMES_LEMMATISES:
                    tt = new HashSet<Terme>(txt.getTousLemmes());
                    break;
                default:
                    tt = txt.getTousTermes();;
            }
            for(Terme t: tt) {
                int n = 0;
                switch(traitement) {
                    case TERMES_BRUTS:
                        n = txt.getNbOccurrencesTerme(t);
                        break;
                    case TERMES_NORMALISES:
                        n = txt.getNbOccurrencesTermeNormalise(t);
                        break;
                    case TERMES_LEMMATISES:
                        n = txt.getNbOccurrencesLemme((Lemme)t);
                        break;
                    default:
                        n = txt.getNbOccurrencesTerme(t);
                }
                if(!nbOcc.containsKey(t)) {
                    nbOcc.put(t, 0);
                }
                if(n > 0) {
                    nbOcc.put(t, nbOcc.get(t) + n);
                }
            }
        }
        return nbOcc;
    }
    
    public static Map<Terme, Integer> getAllDF(Corpus c, int traitement) {
        Texte[] textes = new Texte[c.getDocuments().length];
        for(int i=0; i<c.getDocuments().length; i++) {
            textes[i] = new Texte(c.getDocuments()[i]);
        }
        Map<Terme, Integer> nbDocs = new HashMap<>();
        for(Texte txt: textes) {
            Set<Terme> tt;
            switch(traitement) {
                case TERMES_BRUTS:
                    tt = txt.getTousTermes();
                    break;
                case TERMES_NORMALISES:
                    tt = txt.getTousTermesNormalises();
                    break;
                case TERMES_LEMMATISES:
                    tt = new HashSet<Terme>(txt.getTousLemmes());
                    break;
                default:
                    tt = txt.getTousTermes();;
            }
            for(Terme t: tt) {
                int n = 0;
                switch(traitement) {
                    case TERMES_BRUTS:
                        n = txt.getNbOccurrencesTerme(t);
                        break;
                    case TERMES_NORMALISES:
                        n = txt.getNbOccurrencesTermeNormalise(t);
                        break;
                    case TERMES_LEMMATISES:
                        n = txt.getNbOccurrencesLemme((Lemme)t);
                        break;
                    default:
                        n = txt.getNbOccurrencesTerme(t);
                }
                if(!nbDocs.containsKey(t)) {
                    nbDocs.put(t, 0);
                }
                if(n > 0) {
                    nbDocs.put(t, nbDocs.get(t) + 1);
                }
            }
        }
        return nbDocs;
    }
    
    
    public static Map<Terme, Double> getAllTF_IDF(Corpus c, int traitement) {
        Texte[] textes = new Texte[c.getDocuments().length];
        for(int i=0; i<c.getDocuments().length; i++) {
            textes[i] = new Texte(c.getDocuments()[i]);
        }
        Map<Terme, Integer> nbOccurrences = new HashMap<>();
        Map<Terme, Integer> nbDocs = new HashMap<>();
        for(Texte txt: textes) {
            Set<Terme> tt;
            switch(traitement) {
                case TERMES_BRUTS:
                    tt = txt.getTousTermes();
                    break;
                case TERMES_NORMALISES:
                    tt = txt.getTousTermesNormalises();
                    break;
                case TERMES_LEMMATISES:
                    tt = new HashSet<Terme>(txt.getTousLemmes());
                    break;
                default:
                    tt = txt.getTousTermes();;
            }
            for(Terme t: tt) {
                int n = 0;
                switch(traitement) {
                    case TERMES_BRUTS:
                        n = txt.getNbOccurrencesTerme(t);
                        break;
                    case TERMES_NORMALISES:
                        n = txt.getNbOccurrencesTermeNormalise(t);
                        break;
                    case TERMES_LEMMATISES:
                        n = txt.getNbOccurrencesLemme((Lemme)t);
                        break;
                    default:
                        n = txt.getNbOccurrencesTerme(t);
                }
                if(!nbOccurrences.containsKey(t)) {
                    nbOccurrences.put(t, 0);
                    nbDocs.put(t, 0);
                }
                nbOccurrences.put(t, nbOccurrences.get(t) + n);
                if(n > 0) {
                    nbDocs.put(t, nbDocs.get(t) + 1);
                }
            }
        }
        
        
        Map<Terme, Double> resultat = new HashMap<>();
        for(Terme t: nbOccurrences.keySet()) {
            if(nbDocs.get(t) > 0) {
                double tf_idf = 1.0 * nbOccurrences.get(t) / nbDocs.get(t);
                resultat.put(t, tf_idf);
            }
        }
        
        return resultat;
    }
}
