package tpri;

import java.io.IOException;
import java.util.Iterator;

import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.stream.file.FileSourceEdge;

public class ReadDblpWithFSE {

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		System.setProperty("org.graphstream.ui", "swing"); // utile si tu visualises

        String filename = "com-dblp.ungraph.txt";

        Graph graph = new SingleGraph("DBLP");
        FileSourceEdge fs = new FileSourceEdge();

        fs.addSink(graph);

        try {
            fs.readAll(filename);  // <-- méthode FileSourceEdge
        } finally {
            fs.removeSink(graph);
        }
        
        //Nombre de noeuds et liens (arêtes)
        int n = graph.getNodeCount();
        int m = graph.getEdgeCount();
        
        double avgDegree = Toolkit.averageDegree(graph);
        double clustering = Toolkit.averageClusteringCoefficient(graph);
        
     // Coefficient de clustering d’un graphe aléatoire G(n,p)
        // p = <k> / (n - 1)
        double p = avgDegree / (n - 1);
        double clusteringRandom = p; // pour un ER, C ≈ p

        System.out.println("Lecture terminée !");
        System.out.println("Nombre de nœuds : " + n);
        System.out.println("Nombre d'arêtes : " + m);
        

        System.out.printf("Degré moyen : %.6f%n", avgDegree);
        System.out.printf("Clustering coefficient : %.6f%n", clustering);

        System.out.printf("Clustering réseau aléatoire (même n et degré moyen) : %.10f%n", clusteringRandom);
        
     // 4. Distribution des degrés
        System.out.println("4. DISTRIBUTION DES DEGRÉS");
        calculateDegreeDistribution(graph, "degreeDist_dblp.dat");
        System.out.println("Fichier 'degreeDist_dblp.dat' généré pour Gnuplot.");
        
     // 5. Distance moyenne : 
        System.out.println("5.Distance moyenne");
        calculDistMoyenne(graph);

        
	}
        public static void calculateDegreeDistribution(Graph graph, String filename) {
            // 1. Obtenir la distribution des degrées(nombre de nœuds ayant degré k)
            int[] dd = Toolkit.degreeDistribution(graph); //

            try (java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.FileWriter(filename))) {
                // 2. Parcourir chaque degré k
                for (int k = 0; k < dd.length; k++) {
                    if (dd[k] != 0) {
                        // 3. Calculer la probabilité p(k) = N_k / N
                        double prob = (double) dd[k] / graph.getNodeCount(); 

                        // 4. Écrire dans le fichier (format compatible Gnuplot)
                        writer.printf(java.util.Locale.US, "%6d%20.8f%n", k, prob); 
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }
        
        
	}
        
        // 5ème qst : 

        public static void calculDistMoyenne(Graph g) {

               System.out.println("--- Calcul de la Distance Moyenne (1000 echantillons) ---");

               //1000 sommets
               int nbSamples = 1000; 

               // Variables thread-safe pour le calcul parallèle

               java.util.concurrent.atomic.AtomicLong sommeDistances = new java.util.concurrent.atomic.AtomicLong(0);

               java.util.concurrent.atomic.AtomicLong nbPaires = new java.util.concurrent.atomic.AtomicLong(0);

               long[] distributionDistances = new long[100]; 

               int n = g.getNodeCount();

               System.out.println("Lancement de " + nbSamples + " parcours en largeur...");

               System.out.println("En cours d'exécution cela peut prendre 2 à 3 minutes");

               long debut = System.currentTimeMillis();

               // PARALLÉLISATION : On lance les 1000 BFS en même temps sur tous les coeurs

               java.util.stream.IntStream.range(0, nbSamples).parallel().forEach(i -> {

                   // Choix aléatoire source

                   Node source = g.getNode(java.util.concurrent.ThreadLocalRandom.current().nextInt(n));

                   java.util.Map<Node, Integer> distances = new java.util.HashMap<>();

                   java.util.LinkedList<Node> file = new java.util.LinkedList<>();

                   distances.put(source, 0);

                   file.add(source);


                   // Variables locales pour éviter de bloquer les autres threads

                   long localSomme = 0;

                   long localPaires = 0;

                   long[] localDistr = new long[100];

                   while (!file.isEmpty()) {

                       Node courant = file.poll();

                       int d = distances.get(courant);

                       if (d > 0) {

                           localSomme += d;

                           localPaires++;

                           if (d < localDistr.length) localDistr[d]++;

                       }

                       Iterator<Node> it = courant.neighborNodes().iterator();

                       while (it.hasNext()) {

                           Node voisin = it.next();

                           if (!distances.containsKey(voisin)) {

                               distances.put(voisin, d + 1);

                               file.add(voisin);

                           }

                       }

                   }


                   // Aggrégation des résultats à la fin du parcours

                   sommeDistances.addAndGet(localSomme);

                   nbPaires.addAndGet(localPaires);

                   synchronized (distributionDistances) {

                       for(int k=0; k<localDistr.length; k++) distributionDistances[k] += localDistr[k];

                   }

               });


               long fin = System.currentTimeMillis();

               System.out.println("Le calcul se  termine en " + (fin - debut) + " ms.");

               double avg = (double) sommeDistances.get() / nbPaires.get();

               System.out.printf("Distance Moyenne (estimée) : %.4f%n", avg);

               double avgDegree = Toolkit.averageDegree(g);

               double randomDistance = Math.log(n) / Math.log(avgDegree);

               System.out.printf("Distance Moyenne (réseau aléatoire) : %.4f%n", randomDistance);

               // Ecriture du fichier pour Gnuplot

               try (java.io.PrintWriter writer = new java.io.PrintWriter("distances_dblp.dat")) {

                   for (int d = 0; d < distributionDistances.length; d++) {

                       if (distributionDistances[d] > 0) {

                           double proba = (double) distributionDistances[d] / nbPaires.get();

                           writer.printf("%d %.8f%n", d, proba);

                       }

                   }

                   System.out.println("Fichier 'distances_dblp.dat' généré pour Gnuplot.");

               } catch (java.io.FileNotFoundException e) {

                   e.printStackTrace();

               }

           }


}


