package org.example;

import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static org.example.GraphManipulation.*;
import static org.example.Gnuplot.*;

public class Main_TP2 {

    public static void main(String[] args) throws IOException {
        System.setProperty("org.graphstream.ui", "swing");

        // Charger le graphe DBLP
        String path = "src/main/resources/com-dblp.ungraph.txt";
        Graph graph = getTheFileEdge(path);

        int nodeCount = getNodeCount(graph);
        double averageDegree = getAverageDegree(graph);
        System.out.println("Nombre de noeuds : " + nodeCount);
        System.out.println("Degré moyen : " + averageDegree);

        // Paramètres SIS
        double beta = 0.94;  // taux de propagation
        int days = 90;       // 3 mois
        double dailyRecovery = 2.0 / 30; // 2 fois par mois

        // Scénarios
        simulateScenario(graph, beta, dailyRecovery, days, "src/main/gnuplot/TP2/scenario_no_action.dat", 0);
        simulateScenario(graph, beta, dailyRecovery, days, "src/main/gnuplot/TP2/scenario_random_immunization.dat", 1);
        simulateScenario(graph, beta, dailyRecovery, days, "src/main/gnuplot/TP2/scenario_selective_immunization.dat", 2);

        System.out.println("Simulation terminée. Fichiers .dat prêts pour Gnuplot.");
    }

    /**
     * Scénarios de simulation
     * type:
     * 0 = aucun anti-virus
     * 1 = immunisation aléatoire 50%
     * 2 = immunisation sélective 50%
     */
    public static void simulateScenario(Graph originalGraph, double beta, double recoveryRate, int days,
                                        String outputFile, int type) throws IOException {

        // Créer une copie du graphe pour chaque scénario
        Graph graph = cloneGraph(originalGraph);

        Random rand = new Random();

        // Initialiser tous les nœuds
        for (Node n : graph) {
            n.setAttribute("state", "S");
            n.setAttribute("immune", false);
        }

        // Patient zéro
        Node patientZero = graph.getNode(rand.nextInt(graph.getNodeCount()));
        patientZero.setAttribute("state", "I");

        // Immunisation
        if (type == 1) { // aléatoire 50%
            for (Node n : graph) {
                if (rand.nextDouble() < 0.5) n.setAttribute("immune", true);
            }
        } else if (type == 2) { // sélective 50%
            for (Node n : graph) {
                if (rand.nextDouble() < 0.5) {
                    List<Node> neighbors = new ArrayList<>();
                    for (Edge e : n.edges().toList()) {
                        neighbors.add(e.getOpposite(n));
                    }
                    if (!neighbors.isEmpty()) {
                        Node neighbor = neighbors.get(rand.nextInt(neighbors.size()));
                        neighbor.setAttribute("immune", true);
                    }
                }
            }
        }

        List<Double> infectedFraction = new ArrayList<>();
        // Simulation jour par jour
        for (int day = 0; day < days; day++) {
            List<Node> newInfected = new ArrayList<>();
            for (Node n : graph) {
                if (n.getAttribute("state").equals("I")) {
                    for (Edge e : n.edges().toList()) {
                        Node neighbor = e.getOpposite(n);
                        if (neighbor.getAttribute("state").equals("S") && !neighbor.getAttribute("immune",Boolean.class)) {
                            if (rand.nextDouble() < beta) {
                                newInfected.add(neighbor);
                            }
                        }
                    }
                    // guérison possible
                    if (rand.nextDouble() < recoveryRate) {
                        n.setAttribute("state", "S");
                    }
                }
            }

            // appliquer les nouvelles infections
            for (Node n : newInfected) {
                n.setAttribute("state", "I");
            }

            // calculer fraction infectée
            long countI = graph.nodes().filter(n -> n.getAttribute("state").equals("I")).count();
            infectedFraction.add(countI * 1.0 / graph.getNodeCount());

        }

        calculateAverageDegreeByImmunity(graph,type , outputFile);
        // Sauvegarder dans un fichier .dat pour Gnuplot
        saveDatSingle(outputFile, infectedFraction);
        System.out.println("Fichier " + outputFile + " généré pour le scénario " + type);
    }

    /**
     * Copie un graphe pour ne pas modifier l’original
     */
    public static Graph cloneGraph(Graph original) {
        Graph copy = new org.graphstream.graph.implementations.SingleGraph("Copy");
        for (Node n : original) {
            copy.addNode(n.getId());
        }
        original.edges().forEach(e -> copy.addEdge(e.getId(), e.getSourceNode().getId(), e.getTargetNode().getId()));
        return copy;
    }

    public static void calculateAverageDegreeByImmunity(Graph graph,Integer type , String outputFile) {
        double avgImmune ;
        double avgNonImmune ;

        if (type == 1 || type == 2) {
            double sumImmune = 0, sumNonImmune = 0;
            int countImmune = 0, countNonImmune = 0;

            for (Node n : graph) {
                boolean immune = n.getAttribute("immune", Boolean.class);
                int degree = n.getDegree();

                if (immune) {
                    sumImmune += degree;
                    countImmune++;
                } else {
                    sumNonImmune += degree;
                    countNonImmune++;
                }
            }

            avgImmune = countImmune > 0 ? sumImmune / countImmune : 0;
            avgNonImmune = countNonImmune > 0 ? sumNonImmune / countNonImmune : 0;
            try {
                saveDatMultipleList(
                        outputFile.replace(".dat", "_degree_avg.dat"),
                        List.of(
                                List.of(avgImmune),
                                List.of(avgNonImmune)
                        )
                );
            }catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

