package fr.univ.dblp.analysis;

import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Graph;

/**
 * Calcule les métriques de base d'un réseau (Question 2).
 *
 * Cette classe permet de calculer et d'afficher les mesures fondamentales
 * d'un réseau: nombre de nœuds, nombre d'arêtes, degré moyen et coefficient
 * de clustering. Elle compare également ces métriques avec un réseau aléatoire
 * théorique de même taille.
 *
 * @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 BasicMetrics {

    /**
     * Retourne le nombre de nœuds du graphe.
     *
     * @param g Le graphe à analyser
     * @return Le nombre de nœuds
     */
    public static int getNodeCount(Graph g) {
        return g.getNodeCount();
    }

    /**
     * Retourne le nombre d'arêtes du graphe.
     *
     * @param g Le graphe à analyser
     * @return Le nombre d'arêtes
     */
    public static int getEdgeCount(Graph g) {
        return g.getEdgeCount();
    }

    /**
     * Calcule le degré moyen du graphe.
     *
     * @param g Le graphe à analyser
     * @return Le degré moyen
     */
    public static double getAverageDegree(Graph g) {
        return Toolkit.averageDegree(g);
    }

    /**
     * Calcule le coefficient de clustering du graphe.
     *
     * Le coefficient de clustering mesure la probabilité que deux voisins
     * d'un nœud soient également voisins entre eux (formation de triangles).
     *
     * @param g Le graphe à analyser
     * @return Le coefficient de clustering moyen
     */
    public static double getClusteringCoefficient(Graph g) {
        return Toolkit.averageClusteringCoefficient(g);
    }

    /**
     * Calcule le coefficient de clustering théorique d'un réseau aléatoire.
     *
     * Pour un réseau aléatoire de même taille et même degré moyen,
     * le coefficient de clustering théorique est: C = <k> / N
     *
     * @param n Nombre de nœuds du réseau
     * @param avgDegree Degré moyen du réseau
     * @return Coefficient de clustering théorique
     */
    public static double theoreticalRandomClusteringCoeff(int n, double avgDegree) {
        return avgDegree / n;
    }

    /**
     * Affiche toutes les métriques de base d'un réseau.
     *
     * Cette méthode calcule et affiche le nombre de nœuds, d'arêtes,
     * le degré moyen et le coefficient de clustering. Elle compare
     * également le clustering avec un réseau aléatoire théorique.
     *
     * @param g Le graphe à analyser
     * @param networkName Le nom du réseau (pour l'affichage)
     */
    public static void printMetrics(Graph g, String networkName) {
        ResultsPrinter.printHeader("QUESTION 2: Mesures de base - " + networkName);

        int nodes = getNodeCount(g);
        int edges = getEdgeCount(g);
        double avgDegree = getAverageDegree(g);
        double clustering = getClusteringCoefficient(g);

        ResultsPrinter.printMetric("Nombre de nœuds", nodes);
        ResultsPrinter.printMetric("Nombre d'arêtes", edges);
        ResultsPrinter.printMetric("Degré moyen", avgDegree);
        ResultsPrinter.printMetric("Coefficient de clustering", clustering);

        // Comparaison avec un réseau aléatoire théorique
        double theoreticalClustering = theoreticalRandomClusteringCoeff(nodes, avgDegree);
        ResultsPrinter.printSeparator();
        ResultsPrinter.printInfo("Comparaison avec un réseau aléatoire de même taille:");
        ResultsPrinter.printMetric("Clustering théorique (aléatoire)", theoreticalClustering);

        double ratio = clustering / theoreticalClustering;
        ResultsPrinter.printMetric("Ratio (DBLP / aléatoire)", ratio);

        if (ratio > 1000) {
            ResultsPrinter.printInfo(
                String.format("Le clustering du réseau DBLP est %.0fx plus élevé qu'un réseau aléatoire!",
                            ratio));
        }
    }

    /**
     * Analyse et affiche les métriques de base d'un graphe.
     *
     * Cette méthode orchestre l'analyse complète en appelant printMetrics()
     * et en affichant le temps d'exécution total.
     *
     * @param g Le graphe à analyser
     * @param networkName Le nom du réseau (pour l'affichage)
     */
    public static void analyze(Graph g, String networkName) {
        long startTime = System.currentTimeMillis();
        printMetrics(g, networkName);
        ResultsPrinter.printElapsedTime(startTime);
    }
}
