import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.DefaultGraph;
import org.graphstream.stream.file.FileSource;
import org.graphstream.stream.file.FileSourceEdge;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

public class Propagation {
    private Graph g;

    public Propagation(Graph g) throws IOException {
        this.g = g;
    }

    public Graph getG(){
        return this.g;
    }

    public void enregistrerLesDonnees(String filename, String contenu) {
        try {
            String filepath = System.getProperty("user.dir") + File.separator + "dataPropagation/" + filename + ".dat";
            System.out.println("Ecriture du fichier : " + filepath);
            FileWriter fw = new FileWriter(filepath);
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(contenu);
            bw.close();
        } catch ( IOException e) {
            e.printStackTrace();
        }
    }


    //Condition1:on ne fait rien pour empêcher l'épidémie
    public String simulation1(){
        String contenu = "";
        //trois mois,càd 90 jours
        int nbJour = 90;

        for(Node node:this.getG())//ajouter une attribut pour chaque noeud
            node.addAttribute("health","healthy");

        Node patientZero = Toolkit.randomNode(this.getG());//choisir le premier individu infecté
        patientZero.addAttribute("health","infected");

        ArrayList<Node> infected = new ArrayList<>();//pour stocker les individus infectés qui s'occupe de propager le virus chaque jour
        infected.add(patientZero);
        //un tableau pour stocker les individus de l'etat "healthy" en l'etat "infected" ou de l'etat "infected" en l'etat "healthy"
        ArrayList<Node> temp = new ArrayList<>();
        long[] donneesChaqueJour = new long[nbJour];
        Random r = new Random();
        for(int i=0;i<nbJour;i++){
            for(Node node:infected){//parcourir tous les individus infectes
                if(!temp.contains(node)) temp.add(node);
                if(r.nextInt(7)+1==1) {//la probabilité de recevoir le mail pour chaque voisin de l'individu infecté est 1/7
                    for(Edge e:node){
                        Node voisin = e.getOpposite(node);//obtenir tous les voisins de noeud node
                        if(!temp.contains(voisin)){
                            voisin.setAttribute("health","infected");
                            temp.add(voisin);
                        }
                    }
                }
            }
            //vider le tableau infected,prepare les individus infectés pour lendemain
            infected.clear();
            //mettre à jour
            for(Node node1:temp){
                if(r.nextInt(15)+1==1)
                    node1.setAttribute("health", "healthy");
                else infected.add(node1);
            }
            contenu += (i+1)+" "+infected.size()+"\n";
        }
        return contenu;
    }

    //Condition2:
    public String simulation2() {
        String contenu = "";
        Random r = new Random();
        //trois mois,càd 90 jours
        int nbJour = 90;

        for(Node node:this.getG())//ajouter une attribut pour chaque noeud
            node.addAttribute("health","healthy");

        //convaincre 50 % des individus de mettre à jour en permanence leur anti-virus (immunisation aléatoire)
        List<Node> l = Toolkit.randomNodeSet(this.getG(),this.getG().getNodeCount()/2);
        for(Node node:l) node.addAttribute("health","immunise");

        int k = r.nextInt(l.size());//k est pour choisir un node comme le patient zero
        Node patientZero = l.get(k);//choisir le premier individu infecté
        patientZero.addAttribute("health","infected");

        ArrayList<Node> infected = new ArrayList<>();//pour stocker les individus infectés qui s'occupe de propager le virus chaque jour
        infected.add(patientZero);
        //un tableau pour stocker les individus de l'etat "healthy" en l'etat "infected" ou de l'etat "infected" en l'etat "healthy"
        ArrayList<Node> temp = new ArrayList<>();

        for(int i=0;i<nbJour;i++){
            for(Node node:infected){//parcourir tous les individus infectes
                if(!temp.contains(node)) temp.add(node);
                if(r.nextInt(7)+1==1) {//la probabilité de recevoir le mail pour chaque voisin de l'individu infecté est 1/7
                    for(Edge e:node){
                        Node voisin = e.getOpposite(node);//obtenir tous les voisins de noeud node
                        if(!temp.contains(voisin) && !l.contains(voisin)){
                            voisin.setAttribute("health","infected");
                            temp.add(voisin);
                        }
                    }
                }
            }
            //vider le tableau infected,prepare les individus infectés pour lendemain
            infected.clear();
            //mettre à jour
            for(Node node1:temp){
                if(r.nextInt(15)+1==1)
                    node1.setAttribute("health", "healthy");
                else infected.add(node1);
            }
            contenu += (i+1)+" "+infected.size()+"\n";
        }
        return contenu;
    }

    //question 2(condition 3) et question 3
    public String simulation3() {
        String contenu = "";
        Random r = new Random();
        //trois mois,càd 90 jours
        int nbJour = 90;

        for(Node node:this.getG())//ajouter une attribut pour chaque noeud
            node.addAttribute("health","healthy");

        double someDegreGroupe0 = 0;
        double someDegreGroupe1 = 0;
        ArrayList<Node> immunise = new ArrayList<>();
        //convaincre 50 % des individus de convaincre un de leurs contacts de mettre à jour en permanence son anti-virus (immunisation sélective)
        List<Node> l = Toolkit.randomNodeSet(this.getG(),this.getG().getNodeCount()/2);//l stocke les 50 % des individus
        //remplir le tableau immunise
        for(Node node:l){
            Node nodeImmunise = node.getEdge(r.nextInt(node.getDegree())).getOpposite(node);//choisir un des contacts pour les 50 % des individus
            nodeImmunise.setAttribute("health","immunise");
            immunise.add(nodeImmunise);
            someDegreGroupe0 += node.getDegree();
            someDegreGroupe1 += nodeImmunise.getDegree();
        }

        //question 3
        System.out.println("le degré moyen des groupes 0 est :"+someDegreGroupe0/l.size());
        System.out.println("le degré moyen des groupes 1 est :"+someDegreGroupe1/l.size());

        int k = r.nextInt(immunise.size());//k est pour choisir un node comme le patient zero
        Node patientZero = immunise.get(k);//choisir le premier individu infecté
        patientZero.addAttribute("health","infected");

        ArrayList<Node> infected = new ArrayList<>();//pour stocker les individus infectés qui s'occupe de propager le virus chaque jour
        infected.add(patientZero);
        //un tableau pour stocker les individus de l'etat "healthy" en l'etat "infected" ou de l'etat "infected" en l'etat "healthy"
        ArrayList<Node> temp = new ArrayList<>();

        for(int i=0;i<nbJour;i++){
            for(Node node:infected){//parcourir tous les individus infectes
                if(!temp.contains(node)) temp.add(node);
                if(r.nextInt(7)+1==1) {//la probabilité de recevoir le mail pour chaque voisin de l'individu infecté est 1/7
                    for(Edge e:node){
                        Node voisin = e.getOpposite(node);//obtenir tous les voisins de noeud node
                        if(!temp.contains(voisin) && !l.contains(voisin)){
                            voisin.setAttribute("health","infected");
                            temp.add(voisin);
                        }
                    }
                }
            }
            //vider le tableau infected,prepare les individus infectés pour lendemain
            infected.clear();
            //mettre à jour
            for(Node node1:temp){
                if(r.nextInt(15)+1==1)
                    node1.setAttribute("health", "healthy");
                else infected.add(node1);
            }
            contenu += (i+1)+" "+infected.size()+"\n";
        }
        return contenu;
    }

    public double seuil(int nb){
        int[] dd = Toolkit.degreeDistribution(this.getG());//la valeur de la cellule le nombre de nœuds ayant ce degré
        double degreMoyen = Toolkit.averageDegree(this.getG());//Le degré moyen de ce graphe
        double degreCarreMoyen = 0;
        for (int i = 0; i < dd.length; i++) {
            if (dd[i] != 0)
                degreCarreMoyen += Math.pow(i, 2) * ((double) dd[i] / nb);
        }
        return degreMoyen/degreCarreMoyen;
    }

    public static void main(String[] args) throws IOException {
        System.out.println("Projet propagation dans des réseaux :");
        String filePath = "Data/com-dblp.ungraph.txt";
        Graph g = new DefaultGraph("g");
        FileSource fs = new FileSourceEdge();
        fs.addSink(g);
        try {
            fs.readAll(filePath);
            //g.display();
        } catch( IOException e) {
            e.printStackTrace();
        } finally {
            fs.removeSink(g);
        }
        Propagation p = new Propagation(g);
        System.out.println("*************************************************");
        System.out.println("La question numéro 1: ");
        double  β = 1./7;//probabilité de transmission dans une unité de temps
        double µ = 1./15;//le taux pour redevenir susceptibles
        System.out.println("Le taux de propagation du virus est : "+β/µ);
        System.out.println("Le seuil épidémique du réseau : "+p.seuil(g.getNodeCount()));
        double degreMoyen = Toolkit.averageDegree(g);//Le degré moyen de ce graphe
        System.out.println("Le seuil théorique d'un réseau aléatoire du même degré moyen : "+1/(degreMoyen+1));
        System.out.println("*************************************************");
        System.out.println("La premier simulation :");
        //p.enregistrerLesDonnees("scenario1",p.simulation1());
        System.out.println("*************************************************");
        System.out.println("La deuxième simulation :");
        //p.enregistrerLesDonnees("scenario2",p.simulation2());
        System.out.println("*************************************************");
        System.out.println("La 3ieme simulation :");
        p.enregistrerLesDonnees("scenario3",p.simulation3());
    }

}