package fr.univ.dblp.generators;

import fr.univ.dblp.analysis.*;
import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.algorithm.generator.BarabasiAlbertGenerator;
import org.graphstream.algorithm.generator.RandomGenerator;

/**
 * Génère des réseaux synthétiques pour comparaison (Question 6).
 *
 * Cette classe permet de générer des réseaux aléatoires (modèle d'Erdős-Rényi)
 * et des réseaux à attachement préférentiel (modèle de Barabási-Albert) pour
 * les comparer avec le réseau DBLP réel.
 *
 * @author Hamadou BA
 * @see <a href="https://www-apps.univ-lehavre.fr/forge/bh243413/tp2-ri-mesures-de-reseaux-interaction.git">Dépôt Git</a>
 */
public class NetworkGenerator {

    /**
     * Génère un réseau aléatoire avec le modèle d'Erdős-Rényi.
     *
     * Dans ce modèle, chaque paire de nœuds est connectée avec une probabilité
     * uniforme, produisant une distribution de degrés de type Poisson.
     *
     * @param n Nombre de nœuds
     * @param avgDegree Degré moyen souhaité
     * @return Le graphe aléatoire généré
     */
    public static Graph generateRandomGraph(int n, double avgDegree) {
        ResultsPrinter.printInfo(
            String.format("Génération d'un réseau aléatoire: N=%,d, <k>=%.2f", n, avgDegree));

        Graph g = new SingleGraph("Random");
        g.setStrict(false);
        g.setAutoCreate(true);

        // Calcul du nombre d'arêtes cible
        int targetEdges = (int) Math.round((n * avgDegree) / 2.0);

        RandomGenerator gen = new RandomGenerator(avgDegree);
        gen.addSink(g);
        gen.begin();

        // Génération des nœuds
        for (int i = 0; i < n && g.getEdgeCount() < targetEdges; i++) {
            gen.nextEvents();
        }

        gen.end();

        ResultsPrinter.printSuccess(
            String.format("Réseau aléatoire généré: %,d nœuds, %,d arêtes",
                        g.getNodeCount(), g.getEdgeCount()));

        return g;
    }

    /**
     * Génère un réseau Barabási-Albert (attachement préférentiel).
     *
     * Dans ce modèle, les nouveaux nœuds se connectent préférentiellement
     * aux nœuds déjà bien connectés ("rich get richer"), produisant une
     * distribution de degrés en loi de puissance.
     *
     * @param n Nombre de nœuds
     * @param edgesPerNode Nombre d'arêtes créées par nouveau nœud
     * @return Le graphe Barabási-Albert généré
     */
    public static Graph generateBarabasiAlbert(int n, int edgesPerNode) {
        ResultsPrinter.printInfo(
            String.format("Génération d'un réseau Barabási-Albert: N=%,d, m=%d", n, edgesPerNode));

        Graph g = new SingleGraph("Barabasi-Albert");
        g.setStrict(false);
        g.setAutoCreate(true);

        BarabasiAlbertGenerator gen = new BarabasiAlbertGenerator(edgesPerNode);
        gen.addSink(g);
        gen.begin();

        for (int i = 0; i < n; i++) {
            gen.nextEvents();

            if ((i + 1) % 10000 == 0) {
                ResultsPrinter.printInfo(
                    String.format("  Progression: %,d/%,d nœuds générés", i + 1, n));
            }
        }

        gen.end();

        ResultsPrinter.printSuccess(
            String.format("Réseau Barabási-Albert généré: %,d nœuds, %,d arêtes",
                        g.getNodeCount(), g.getEdgeCount()));

        return g;
    }

    /**
     * Exécute toutes les analyses sur un réseau généré.
     *
     * Cette méthode privée applique l'ensemble des analyses (métriques de base,
     * connexité, distribution des degrés, distances) sur un réseau synthétique.
     *
     * @param g Le graphe à analyser
     * @param networkName Nom du réseau
     * @param baseFilename Nom de base pour les fichiers d'export
     */
    private static void runAllAnalyses(Graph g, String networkName, String baseFilename) {
        ResultsPrinter.printHeader("ANALYSE COMPLÈTE: " + networkName);

        // Question 2: Métriques de base
        BasicMetrics.analyze(g, networkName);

        // Question 3: Connexité
        ConnectivityAnalyzer.analyzeConnectivity(g, networkName);

        // Question 4: Distribution des degrés
        DegreeAnalyzer.analyze(g, networkName, baseFilename);

        // Question 5: Distance moyenne (avec échantillon réduit pour les réseaux générés)
        if (g.getNodeCount() > 100000) {
            ResultsPrinter.printWarning(
                "Réseau trop grand, analyse de distance ignorée pour gain de temps");
        } else {
            DistanceAnalyzer.analyze(g, networkName, baseFilename);
        }
    }

    /**
     * Effectue l'analyse complète de la Question 6.
     *
     * Cette méthode génère un réseau aléatoire et un réseau Barabási-Albert,
     * effectue toutes les analyses sur ces réseaux, et les compare avec DBLP
     * dans un tableau récapitulatif.
     *
     * @param dblpGraph Le graphe DBLP de référence
     */
    public static void analyze(Graph dblpGraph) {
        long startTime = System.currentTimeMillis();
        ResultsPrinter.printHeader("QUESTION 6: Comparaison avec générateurs");

        int n = dblpGraph.getNodeCount();
        double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);

        // Utilisation d'une taille réduite pour les performances
        int generatedSize = Math.min(n, 50000);
        int edgesPerNode = (int) Math.round(avgDegree / 2.0);

        ResultsPrinter.printInfo(
            String.format("Paramètres DBLP: N=%,d, <k>=%.2f", n, avgDegree));
        ResultsPrinter.printInfo(
            String.format("Taille des réseaux générés: N=%,d (réduit pour performance)", generatedSize));

        // Génération du réseau aléatoire
        ResultsPrinter.printSubHeader("Génération réseau aléatoire");
        Graph randomGraph = generateRandomGraph(generatedSize, avgDegree);

        // Génération du réseau Barabási-Albert
        ResultsPrinter.printSubHeader("Génération réseau Barabási-Albert");
        Graph baGraph = generateBarabasiAlbert(generatedSize, Math.max(1, edgesPerNode));

        // Exécution des analyses
        runAllAnalyses(randomGraph, "Aléatoire", "random");
        runAllAnalyses(baGraph, "Barabási-Albert", "ba");

        // Affichage du tableau comparatif
        printComparisonTable(dblpGraph, randomGraph, baGraph);

        ResultsPrinter.printElapsedTime(startTime);
    }

    /**
     * Affiche un tableau comparatif des trois réseaux.
     *
     * Cette méthode privée crée un tableau récapitulatif comparant les propriétés
     * principales (nombre de nœuds, arêtes, degré moyen, clustering) des trois
     * réseaux: DBLP, aléatoire et Barabási-Albert.
     *
     * @param dblp Graphe DBLP
     * @param random Graphe aléatoire
     * @param ba Graphe Barabási-Albert
     */
    private static void printComparisonTable(Graph dblp, Graph random, Graph ba) {
        ResultsPrinter.printHeader("TABLEAU COMPARATIF");

        ResultsPrinter.printComparisonHeader("DBLP", "Aléatoire", "Barabási-Albert");

        ResultsPrinter.printComparisonRow("Nœuds",
            dblp.getNodeCount(),
            random.getNodeCount(),
            ba.getNodeCount());

        ResultsPrinter.printComparisonRow("Arêtes",
            dblp.getEdgeCount(),
            random.getEdgeCount(),
            ba.getEdgeCount());

        ResultsPrinter.printComparisonRow("Degré moyen",
            BasicMetrics.getAverageDegree(dblp),
            BasicMetrics.getAverageDegree(random),
            BasicMetrics.getAverageDegree(ba));

        ResultsPrinter.printComparisonRow("Clustering",
            BasicMetrics.getClusteringCoefficient(dblp),
            BasicMetrics.getClusteringCoefficient(random),
            BasicMetrics.getClusteringCoefficient(ba));

        ResultsPrinter.printSeparator();

        ResultsPrinter.printInfo("CONCLUSIONS:");
        ResultsPrinter.printInfo("  - Aléatoire: clustering très faible, pas de structure");
        ResultsPrinter.printInfo("  - Barabási-Albert: loi de puissance mais clustering faible");
        ResultsPrinter.printInfo("  - DBLP: loi de puissance ET clustering élevé (propriété unique!)");
    }
}
