package rh213424;

import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import static org.graphstream.algorithm.Toolkit.averageDegree;
import static org.graphstream.algorithm.Toolkit.randomNode;

public class Propagation {
  /**
   * graphe du réseau
   */
  private Graph graph;

  public Propagation(Graph graph) {
    this.graph = graph;
  }
  /**
   * calcul la distribution des degrés <k²>
   * @return double
   */
  public  double  distributionDegre(){
    double d = 0.0;
    int[] tabDistributionDegres= Toolkit.degreeDistribution(graph);
    for (int i = 0; i < tabDistributionDegres.length; i++) {
      double moyenneDegre = (double) tabDistributionDegres[i] / graph.getNodeCount();
      if (tabDistributionDegres[i] > 0) {
        d += Math.pow(i, 2) * moyenneDegre;
      }
    }
    return d;
  }

  /**
   * Calcul du taux de propagation λ = β / μ
   */
  public static void tauxPropagation(){
    double beta = 1.0/7.0;
    double gama = 1.0/14.0;
    double taux = beta/gama;
    System.out.println("Le taux de propagation du virus est : "+taux);
  }

  /**
   * Calcule et affiche le seuil épidémique
   */
  public void seuilEpidemique(){
    double k2 = distributionDegre();
    System.out.println("La distribution des degrés <k²> de notre réseau est :"+k2);
    double k = averageDegree(graph);
    double seuil = k/k2;
    System.out.println("Le seuil épidémique du réseau est : "+seuil);
  }

  /**
   * Simulation premier scenario
   */
  public void scenario1() throws IOException {
    //liste pour stocker les infectes
    ArrayList<Node> infectes = new ArrayList<>();
    Random random = new Random();
    //Pour l'ecriture du fichier
    StringBuilder data = new StringBuilder();
    //Tout les noeuds sont sains au debut
    for (Node node : graph) {
      node.setAttribute("santé", "sain");
    }
    //generation d'un random premier infecté
    Node patientZeo = randomNode(graph);
    infectes.add(patientZeo);
    patientZeo.setAttribute("santé", "infecté");
    //Pour stocker le nombre d'infectes par jour
    int[] nbrInfect = new int[84];
    //Pour chaque jour
    for (int i = 0; i < 84; i++) {
      for (Node n : graph) {
        //Si le noeud est infecté on infecte tout les vosins collaborateurs
        if (n.getAttribute("santé") == "infecté") {
          for (Edge e : n) {
            Node voisin = e.getOpposite(n);
            //Probabilité 1/3 pour qu'un noeud soit infecté
            if (random.nextInt(3) == 1)  {
              if (voisin.getAttribute("santé") == "sain") {
                voisin.setAttribute("santé", "infecté");
                infectes.add(voisin);
              }
            }
          }
        }
      }
      Iterator<Node> it = infectes.iterator();
      //Probabilité de 1/14 pour qu'un collaborateur met à jour son antivirus
      while (it.hasNext()) {
        if (random.nextInt(20) == 1)  {
          it.next().setAttribute("santé", "sain");
          it.remove();
        }
        else it.next();
      }
      //On stocke le nombre d'infectés par jour
      nbrInfect[i] = infectes.size();
    }
    for (int i = 0; i < 84; i++) {
      int nb = i+1;
      System.out.println("jour " + nb+ ":" + nbrInfect[i]);
      data.append(nb).append(" ").append(nbrInfect[i]).append(System.getProperty("line.separator"));
    }
    creerFichier(data,1);
  }
  /**
   * Simulation scénario2
   */
  public void scenario2() throws IOException {
    int nbImmunise = 0;
    StringBuilder data = new StringBuilder();
    for(Node n : graph) {
      if ((Math.random() < (1.0/2.0))){
        n.setAttribute("immunise", true);
        nbImmunise++;
      }
    }
    //generation d'un random premier infecté
    Node patientZeo = randomNode(graph);
    patientZeo.setAttribute("santé", "infecté");
    int nbInfect = 1;
      for(int i = 0; i<84; i++){
        for (Node n : graph){
          if(n.hasAttribute("infecte")){
            for(Edge e : n){
              double rand = Math.random();
              Node voisin = e.getOpposite(n);
              if(rand < 1.0/7.0 && !voisin.hasAttribute("immunise")){
                if(!voisin.hasAttribute("infecte")){
                  voisin.setAttribute("infecte", true);
                  nbInfect++;
                }
              }
            }
          }
          double rand = Math.random();
          if(rand < 1.0/14.0){
            if(n.hasAttribute("infecte")){
              n.removeAttribute("infecte");
              nbInfect--;
            }
          }
        }
        System.out.println(i +" "+ nbInfect);
        data.append(i).append(" ").append(nbInfect).append(System.getProperty("line.separator"));
      }
    creerFichier(data,2);
  }

  /**
   * Simulation scenario 3
   * @throws IOException
   */
  public void scenario3() throws IOException {
    int nbImmunise = 0;
    double moyDegregroupe0 = 0;
    double moyDegregroupe1 = 0;
    StringBuilder data = new StringBuilder();
    for(Node n : graph) {
      if ((Math.random() < (1.0/2.0)) && n.edges().count() != 0) {
        int numAle = (int)(Math.random() * n.edges().count());
        Node voisin = n.getEdge(numAle).getOpposite(n);
        voisin.setAttribute("immunise", true);
        //on prend les degrés des noeuds du groupe0 et groupe1(les voisins immunisés)
        moyDegregroupe0 += n.getDegree();
        moyDegregroupe1 += voisin.getDegree();
        nbImmunise++;
      }
    }
    Node s = graph.getNode(0);
    s.setAttribute("infecte", true);
    int nbInfect = 1;
    for(int i = 0; i<84; i++){
      for (Node n : graph){
        if(n.hasAttribute("infecte")){
          for(Edge e : n){
            double rand = Math.random();
            Node voisin = e.getOpposite(n);
            if(rand < 1.0/7.0 && !voisin.hasAttribute("immunise")){
              if(!voisin.hasAttribute("infecte")){
                voisin.setAttribute("infecte", true);
                nbInfect++;
              }
            }
          }
        }
        double rand = Math.random();
        if(rand < 1.0/14.0){
          if(n.hasAttribute("infecte")){
            n.removeAttribute("infecte");
            nbInfect--;
          }
        }
      }
      System.out.println(i +" "+ nbInfect);
      data.append(i).append(" ").append(nbInfect).append(System.getProperty("line.separator"));
    }
    moyDegregroupe0 = moyDegregroupe0/nbImmunise;
    moyDegregroupe1 = moyDegregroupe1/nbImmunise;

    System.out.print("Le degré moyen du groupe 0 : "+ moyDegregroupe0 +"\n");
    System.out.print("Le degré moyen du groupe 1: "+ moyDegregroupe1 +"\n");

    creerFichier(data,3);
  }
  /**
   * Méthode pour l'écriture des données pour chaque scénario
   * @param data
   * @param num
   * @throws IOException
   */
  public void creerFichier(StringBuilder data,int num) throws IOException {
    File fichier = new File("./resources/scenarios/scenario"+num+".dat");
    if (!fichier.exists()) {
      fichier.createNewFile();
    }
    FileWriter fw = new FileWriter(fichier.getAbsoluteFile(),false);
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(data.toString());
    bw.close();
  }
}