import java.util.List;

import org.graphstream.graph.BreadthFirstIterator;
import org.graphstream.graph.Graph;
import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Node;
/**
 * La classe TP2 permet de créer les 3 graphes:
 * - un avec le fichier
 * - en utilisant Barabasi Albert
 * - en générant aléatoirement
 * Fait les appels aux fonctions créées dans la classe "CréationFichier"
 * pour générer les fichiers qui permettent de créer les graphes
 */

public class TP2
{
	public static void main(String[] args) {

		Graph grapheFichier   = GenererGraphe.genererGrapheFichier("com-dblp.ungraph.txt");
		Graph grapheAlbert    = GenererGraphe.genererGrapheBarabasiAlbert(5, grapheFichier.getNodeCount());
		Graph grapheAleatoire =GenererGraphe.genererAleatoire(6.22, grapheFichier.getNodeCount());

		System.out.println(TP2.afficherInformation(grapheFichier)   + "\n");
		System.out.println(TP2.afficherInformation(grapheAlbert)    + "\n");
		System.out.println(TP2.afficherInformation(grapheAleatoire) + "\n");


		//Question 4

		// distribution des degrés
		double[] ddGrapheFichier   = degreeDistribution(grapheFichier);
		double[] ddGrapheAlbert    = degreeDistribution(grapheAlbert);
		double[] ddGrapheAleatoire = degreeDistribution(grapheAleatoire);

		CreationFichier.creerFichier(ddGrapheFichier,"ResultatGrapheFichier");
		CreationFichier.creerFichier(ddGrapheAlbert,"ResultatGrapheAlbert");
		CreationFichier.creerFichier(ddGrapheAleatoire,"ResultatGrapheAleatoire");

		// graphe Linéaire
		CreationFichier.creerGrapheLineaire("GrapheFichier_Lineaire","ResultatGrapheFichier");

		// graphe Log Log
		CreationFichier.creerGrapheLogLog("GrapheFichier_LogLog","ResultatGrapheFichier");

		// graphe distribution poisson
		CreationFichier.creerGraphePoisson("GrapheFichier"  ,"ResultatGrapheFichier");
		CreationFichier.creerGraphePoisson("GrapheAlbert"   ,"ResultatGrapheAlbert");
		CreationFichier.creerGraphePoisson("GrapheAleatoire","ResultatGrapheAleatoire");

		// Distribution des distances

		distanceDistribution(grapheFichier  , "GrapheFichier");
		distanceDistribution(grapheAlbert,   "GrapheAlbert");
		distanceDistribution(grapheAleatoire, "GrapheAleatoire");
	}

	/**
	 * Méthode qui permet de calculer la distribution des distances et de créer son graphe
	 * @param g graphe
	 * @param nomFichier donne le nom du fichier Graphe
	 */
	private static void distanceDistribution(Graph g, String nomFichier)
	{
		// Récupère 1000 noeuds aléatoirement
		List<Node> randomNode= Toolkit.randomNodeSet(g,1000);
		BreadthFirstIterator bfi = new BreadthFirstIterator(randomNode.get(0));

		//Parcours en largeur de notre graphe
		while(bfi.hasNext())
			bfi.next();

		double distance=0;
		double[] distanceDistri = new double[bfi.getDepthMax()+1];
		for (Node n:randomNode )
		{
			//Addition les liens des 1000 noeuds pris au hasard
			if(bfi.getDepthOf(n) != -1)
			{
				distance += bfi.getDepthOf(n);
				//Récupere la distribution des distances
				distanceDistri[bfi.getDepthOf(n)]++;
			}
		}

		double distanceMoyene = distance/1000.0;
		System.out.println("\nNom du graphe: " + g.getId());
		System.out.println("Distane moyenne: " +distanceMoyene);

		System.out.println("L'hypothèse des six degrés de séparation  :" +(distanceMoyene<=6.0));
		double petitMonde = (Math.log(g.getNodeCount())/Math.log(Toolkit.averageDegree(g)));
		System.out.println("ln(N)/ln(<k>) :" + petitMonde);
		for (int k = 0; k < distanceDistri.length; k++) {
			if (distanceDistri[k] != 0) {
				distanceDistri[k] = (double)distanceDistri[k] / 1000;
			}
		}
		CreationFichier.creerGrapheDistance(distanceDistri, nomFichier);
	}

	/**
	 * Méthode qui permet d'afficher les informations d'un graphe
	 * @param g graphe
	 * @return information que l'on va afficher
	 */
	private static String afficherInformation(Graph g)
	{
		return
				"Nom du graphe: " + g.getId() + "\n" +
				"Nombre de noeuds : " + g.getNodeCount() +"\n"+
				"Nombre d'arrêtes : " + g.getEdgeCount()+"\n"+
				"Degré moyen : " + Toolkit.averageDegree(g) +"\n"+
				"Coefficient de clustering moyen : " + Toolkit.averageClusteringCoefficient(g)+"\n"+
				"Coefficient de clustering pour un réseau aléatoire de même taille et du même degré moyen: "+
				Toolkit.averageDegree(g)/g.getNodeCount()+"\n"+
				"Le réseau est connexe : " + Toolkit.isConnected(g)+"\n"+
				"Un réseau aléatoire de la même taille et degré moyen sera-t-il connexe ? " +
				(Toolkit.averageDegree(g) > Math.log(g.getNodeCount()))+"\n"+
				"À partir de quel degré moyen un réseau aléatoire avec cette taille devient connexe ? " +
				Math.log(g.getNodeCount());

	}

	/**
	 * Méthode qui permet de la distribution des distances et de créer son graphe
	 * @param g graphe
	 * @return la distribution des degrés normaliser entre 0 et 1
	 */
	private static double[] degreeDistribution(Graph g)
	{
		int[] dd = Toolkit.degreeDistribution(g);
		double[] degree = new double[dd.length];
		for (int k = 0; k < dd.length; k++) {
			if (dd[k] != 0) {
				degree[k] = (double)dd[k] / g.getNodeCount();
			}
		}
		return degree;
	}
}