/*
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.*;
import fr.cnrs.liris.drim.grt.modele.listes.LemmesRecurrents;
import fr.cnrs.liris.drim.grt.modele.listes.LemmesUsuels;
import fr.cnrs.liris.drim.grt.modele.listes.LemmesVides;

/**
 *
 * @author Sam
 */
public class Citation {
    private Passage citeur;
    private Passage cite;
    private String argumentaire;
    
    public final static int MODE_NORMAL = 0;
    public final static int MODE_NORMALISE = 1;
    public final static int MODE_LEMMATISE = 2;
    
    public final static int TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS = 10;
    public final static int TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS = 7;
    public final static int TOLERANCE_RECURRENTS_SIGNIFICATIFS = 4;
    public final static int TOLERANCE_SIGNIFICATIFS = 1;
    
    
    public Citation(Passage de, Passage dans, String pourquoi) {
        cite = de;
        citeur = dans;
        argumentaire = pourquoi;
    }
    
    public Citation(Passage de, Passage dans) {
        this(de, dans, "");
    }
    
    public Passage getCiteur() {
        return citeur;
    }
    
    public Passage getCite() {
        return cite;
    }
    
    public String getArgumentaire() {
        return argumentaire;
    }
    
    /**
     * Dédoublonne et trie les mots de cette citation. Après exécution de cette méthode,
     * cette Citation contiendra comme citeur et comme cité des Passages
     * uniquement formés de mots.
     * 
     * @param texteGlobalCiteur le texte dans lequel se trouvent les mots du passage citeur
     * @param texteGlobalCite  le texte dans lequel se trouvent les mots du passage cite
     */
    public void dedoublonneEtTrie(Passage texteGlobalCiteur, Passage texteGlobalCite) {
        texteGlobalCiteur.trieMots(citeur);
        texteGlobalCite.trieMots(cite);
    }
    
    /**
     * Fusionne les mots des deux citations. Après exécution de cette méthode,
     * cette Citation contiendra comme citeur et comme cité des Passages
     * uniquement formés de mots, les mots de l'autre citation suivant ceux de 
     * celle-ci.
     * 
     * @param autreCitation une citation à fusionner
     * @param nouvelArgumentaire l'argumentaire qui remplace celui de cette Citation
     */
    public void fusionneAvec(Citation autreCitation, String nouvelArgumentaire) {
        argumentaire = nouvelArgumentaire;
        
        Passage[] motsCiteur1 = citeur.getAllMots();
        Passage[] motsCiteur2 = autreCitation.citeur.getAllMots();
        Passage[] motsCite1 = cite.getAllMots();
        Passage[] motsCite2 = autreCitation.cite.getAllMots();
        
        Map<Coordonnee, Passage> motsCiteur = new HashMap<>();
        int coo = 0;
        for(Passage mot: motsCiteur1) {
            coo++;
            motsCiteur.put(new Coordonnee(OrdreStrict.getInstance(OrdreStrict.MOTS), coo), mot);
        }
        for(Passage mot: motsCiteur2) {
            coo++;
            motsCiteur.put(new Coordonnee(OrdreStrict.getInstance(OrdreStrict.MOTS), coo), mot);
        }
        Map<Coordonnee, Passage> motsCite = new HashMap<>();
        coo = 0;
        for(Passage mot: motsCite1) {
            coo++;
            motsCite.put(new Coordonnee(OrdreStrict.getInstance(OrdreStrict.MOTS), coo), mot);
        }
        for(Passage mot: motsCite2) {
            coo++;
            motsCite.put(new Coordonnee(OrdreStrict.getInstance(OrdreStrict.MOTS), coo), mot);
        }
        citeur = new Passage("", new Passage[]{}, motsCiteur);
        cite = new Passage("", new Passage[]{}, motsCite);
    }
    
    public Terme[] getTermesCommuns(int MODE_QQCH, int TOLERANCE_QQCH) {
        Map<Terme, Integer> termesCite = new HashMap<>();
        for(Passage mot: cite.getAllMots()) {
            Terme t = mot.getContenu();
            boolean prisEnCompte = true;
            if(LemmesVides.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS) {
                prisEnCompte = false;
            } else if(LemmesUsuels.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_RECURRENTS_SIGNIFICATIFS) {
                prisEnCompte = false;
            } else if(LemmesRecurrents.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_SIGNIFICATIFS) {
                prisEnCompte = false;
            }
            if(prisEnCompte) {
                if(MODE_QQCH == MODE_NORMALISE) {
                    t = t.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    t = t.getLemmePrincipal();
                }
                if(termesCite.containsKey(t)) {
                    termesCite.put(t, termesCite.get(t) + 1);
                } else {
                    termesCite.put(t, 1);
                }
            }
        }
        Map<Terme, Integer> termesCiteur = new HashMap<>();
        for(Passage mot: citeur.getAllMots()) {
            Terme t = mot.getContenu();
            boolean prisEnCompte = true;
            if(LemmesVides.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS) {
                prisEnCompte = false;
            } else if(LemmesUsuels.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_RECURRENTS_SIGNIFICATIFS) {
                prisEnCompte = false;
            } else if(LemmesRecurrents.contientTerme(t) && TOLERANCE_QQCH <= TOLERANCE_SIGNIFICATIFS) {
                prisEnCompte = false;
            }
            if(prisEnCompte) {
                if(MODE_QQCH == MODE_NORMALISE) {
                    t = t.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    t = t.getLemmePrincipal();
                }
                if(termesCiteur.containsKey(t)) {
                    termesCiteur.put(t, termesCiteur.get(t) + 1);
                } else {
                    termesCiteur.put(t, 1);
                }
            }
        }
        Set<Terme> tt = new HashSet<>(termesCite.keySet());
        tt.retainAll(termesCiteur.keySet());
        ArrayList<Terme> termes = new ArrayList<>();
        for(Terme t: tt) {
            for(int i=0; i<Math.min(termesCite.get(t), termesCiteur.get(t)); i++) {
                termes.add(t);
            }
        }
        Terme[] termesCite2 = new Terme[termes.size()];
        termes.toArray(termesCite2);
        return termesCite2;
    }
    
    public int getNbTermesCommuns(int MODE_QQCH, int TOLERANCE_QQCH) {
        return getTermesCommuns(MODE_QQCH, TOLERANCE_QQCH).length;
    }
    
    public Terme[] getPlusGrandeChaineDeMotsCommune(int MODE_QQCH, boolean avecMotsVides) {
        ArrayList<Terme> resultat = new ArrayList<>();
        Passage[] lCite = avecMotsVides ? cite.getAllMots() : cite.getAllMotsNonVides();
        Passage[] lCiteur = avecMotsVides ? citeur.getAllMots() : citeur.getAllMotsNonVides();
        for(int i=0; i<lCite.length; i++) {
            for(int j=0; j<lCiteur.length; j++) {
                ArrayList<Terme> resultatPotentiel = new ArrayList<>();
                int a = i;
                int b = j;
                Terme A = lCite[a].getContenu();
                if(MODE_QQCH == MODE_NORMALISE) {
                    A = A.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    A = A.getLemmePrincipal();
                }
                Terme B = lCiteur[b].getContenu();
                if(MODE_QQCH == MODE_NORMALISE) {
                    B = B.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    B = B.getLemmePrincipal();
                }
                while(A.equals(B)) {
                    resultatPotentiel.add(A);
                    a++;
                    b++;
                    if(a >= lCite.length || b >= lCiteur.length) {
                        break;
                    }
                    A = lCite[a].getContenu();
                    if(MODE_QQCH == MODE_NORMALISE) {
                        A = A.getFormeNormale();
                    } else if(MODE_QQCH == MODE_LEMMATISE) {
                        A = A.getLemmePrincipal();
                    }
                    B = lCiteur[b].getContenu();
                    if(MODE_QQCH == MODE_NORMALISE) {
                        B = B.getFormeNormale();
                    } else if(MODE_QQCH == MODE_LEMMATISE) {
                        B = B.getLemmePrincipal();
                    }
                }
                if(resultat.size() < resultatPotentiel.size()) {
                    resultat.clear();
                    resultat.addAll(resultatPotentiel);
                }
            }
        }
        Terme[] resultat2 = new Terme[resultat.size()];
        resultat.toArray(resultat2);
        return resultat2;
    }
    
    public int getTaillePlusGrandeChaineDeMotsCommune(int MODE_QQCH, boolean avecMotsVides) {
        return getPlusGrandeChaineDeMotsCommune(MODE_QQCH, avecMotsVides).length;
    }
    
    public Terme[] getPlusGrandeChaineDeMotsCommune(int MODE_QQCH, int TOLERANCE_QQCH) {
        //System.err.println("Recherche de la plus grande chaîne de mots commune");
        ArrayList<Terme> motsCite = new ArrayList<>();
        ArrayList<Terme> motsCitant = new ArrayList<>();
        Passage[] lCite = cite.getAllMots();
        Passage[] lCitant = citeur.getAllMots();
        for(Passage mot: lCite) {
            if((LemmesVides.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS) || 
               (LemmesUsuels.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS) || 
               (LemmesRecurrents.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_RECURRENTS_SIGNIFICATIFS) || 
               (!LemmesVides.contientTerme(mot.getContenu()) && !LemmesUsuels.contientTerme(mot.getContenu()) && !LemmesRecurrents.contientTerme(mot.getContenu()))) {
                motsCite.add(mot.getContenu());
            }
        }
        for(Passage mot: lCitant) {
            if((LemmesVides.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS) || 
               (LemmesUsuels.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS) || 
               (LemmesRecurrents.contientTerme(mot.getContenu()) && TOLERANCE_QQCH >= TOLERANCE_RECURRENTS_SIGNIFICATIFS) || 
               (!LemmesVides.contientTerme(mot.getContenu()) && !LemmesUsuels.contientTerme(mot.getContenu()) && !LemmesRecurrents.contientTerme(mot.getContenu()))) {
                motsCitant.add(mot.getContenu());
            }
        }
        ArrayList<Terme> resultat = new ArrayList<>();
        for(int i=0; i<motsCite.size(); i++) {
            for(int j=0; j<motsCitant.size(); j++) {
                ArrayList<Terme> resultatPotentiel = new ArrayList<>();
                int a = i;
                int b = j;
                Terme A = motsCite.get(a);
                if(MODE_QQCH == MODE_NORMALISE) {
                    A = A.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    A = A.getLemmePrincipal();
                }
                Terme B = motsCitant.get(b);
                if(MODE_QQCH == MODE_NORMALISE) {
                    B = B.getFormeNormale();
                } else if(MODE_QQCH == MODE_LEMMATISE) {
                    B = B.getLemmePrincipal();
                }
                while(A.equals(B)) {
                    resultatPotentiel.add(A);
                    a++;
                    b++;
                    if(a >= motsCite.size() || b >= motsCitant.size()) {
                        break;
                    }
                    A = motsCite.get(a);
                    if(MODE_QQCH == MODE_NORMALISE) {
                        A = A.getFormeNormale();
                    } else if(MODE_QQCH == MODE_LEMMATISE) {
                        A = A.getLemmePrincipal();
                    }
                    B = motsCitant.get(b);
                    if(MODE_QQCH == MODE_NORMALISE) {
                        B = B.getFormeNormale();
                    } else if(MODE_QQCH == MODE_LEMMATISE) {
                        B = B.getLemmePrincipal();
                    }
                }
                if(resultat.size() < resultatPotentiel.size()) {
                    resultat.clear();
                    resultat.addAll(resultatPotentiel);
                }
            }
        }
        Terme[] resultat0 = new Terme[resultat.size()];
        resultat.toArray(resultat0);
        return resultat0;
    }
    
    public int getTaillePlusGrandeChaineDeMotsCommune(int MODE_QQCH, int TOLERANCE_QQCH) {
        return getPlusGrandeChaineDeMotsCommune(MODE_QQCH, TOLERANCE_QQCH).length;
    }
    
    @Override
    public String toString() {
        return cite.getTexteComplet() + " -----> " + citeur.getTexteComplet() + " (" + argumentaire + ")";
    }
}
