"""
Module principal : Simulation complète des protocoles LEACH et LEACH-C
Supporte à la fois les réseaux statiques et dynamiques.
"""

import random
import json
from datetime import datetime
from node import Node
from leach import LEACH
from leach_c import LEACHC
from config import (
    FIELD_WIDTH, FIELD_HEIGHT, INITIAL_ENERGY, BS_POSITION,
    SCENARIOS, get_num_rounds_for_scenario, DEBUG, ENABLE_MOBILITY
)
import config


class Simulator:
    """
    Contrôleur principal de la simulation.
    Crée les nœuds, lance les protocoles, et collecte les résultats.
    """
    
    def __init__(self, scenario):
        """
        Initialise un simulateur pour un scénario donné.
        
        Args:
            scenario (dict): Configuration du scénario (l, p, n, name)
        """
        self.scenario = scenario
        self.packet_size = scenario["l"]
        self.probability_ch = scenario["p"]
        self.num_nodes = scenario["n"]
        self.scenario_name = scenario["name"]
        
        self.results = {}
        self.nodes = []
    
    def initialize_nodes(self):
        """Crée et initialise les nœuds."""
        self.nodes = []
        
        for i in range(self.num_nodes):
            # Position aléatoire dans le champ
            x = random.uniform(0, FIELD_WIDTH)
            y = random.uniform(0, FIELD_HEIGHT)
            
            node = Node(i, x, y, INITIAL_ENERGY)
            self.nodes.append(node)
    
    def run_protocol(self, protocol_name, protocol_class):
        """
        Lance un protocole et collecte les résultats.
        
        Args:
            protocol_name (str): "LEACH" ou "LEACH-C"
            protocol_class: Classe du protocole (LEACH ou LEACHC)
        
        Returns:
            dict: Métriques et données du protocole
        """
        # Réinitialiser les nœuds
        for node in self.nodes:
            node.energy = INITIAL_ENERGY
            node.is_alive = True
            node.reset_for_round()
        
        # Créer et lancer le protocole
        protocol = protocol_class(self.nodes, self.probability_ch, self.packet_size)
        num_rounds = get_num_rounds_for_scenario(self.num_nodes)
        
        print(f"  Exécution {protocol_name} pour {self.scenario_name}...")
        print(f"    - Packets: {self.packet_size} bits")
        print(f"    - Probabilité: {self.probability_ch}")
        print(f"    - Nœuds: {self.num_nodes}")
        print(f"    - Rounds à exécuter: {num_rounds}")
        
        protocol.run_simulation(num_rounds)
        
        metrics = protocol.get_metrics(num_rounds)
        detailed = protocol.get_detailed_metrics()
        
        print(f"  OK - {protocol_name} terminé")
        print(f"    - Alive nodes: {metrics['final_alive_nodes']}")
        print(f"    - FDN: {metrics['first_dead_node_round']}")
        print(f"    - DLBI: {metrics['dlbi']:.4f}")
        print(f"    - RSPI: {metrics['rspi']:.4f}")
        
        return {
            "protocol": protocol_name,
            "metrics": metrics,
            "detailed": detailed,
        }
    
    def run_simulation(self):
        """
        Lance la simulation complète (LEACH et LEACH-C).
        """
        print(f"\n{'='*60}")
        print(f"Simulation: {self.scenario_name}")
        print(f"{'='*60}")
        
        # Initialiser les nœuds
        self.initialize_nodes()
        
        # Lancer LEACH
        leach_results = self.run_protocol("LEACH", LEACH)
        self.results["LEACH"] = leach_results
        
        # Lancer LEACH-C
        leachc_results = self.run_protocol("LEACH-C", LEACHC)
        self.results["LEACH-C"] = leachc_results
        
        print(f"{'='*60}\n")
        
        return self.results
    
    def get_results(self):
        """Retourne les résultats de la simulation."""
        return self.results


def run_all_scenarios(is_static=False):
    """
    Lance les simulations pour tous les scénarios.

    Args:
        is_static (bool): Si True, désactive la mobilité (mode statique)

    Returns:
        dict: Résultats pour tous les scénarios
    """
    # Définir le mode de mobilité
    config.ENABLE_MOBILITY = not is_static

    all_results = {}
    mode_label = "STATIQUES" if is_static else "DYNAMIQUES"

    print(f"\n{'#'*60}")
    print(f"# SIMULATION LEACH vs LEACH-C - RÉSEAUX {mode_label}")
    print(f"# Démarrage: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"{'#'*60}\n")

    for scenario in SCENARIOS:
        print(f"Scénario: {scenario['name']}")

        simulator = Simulator(scenario)
        results = simulator.run_simulation()

        all_results[scenario["name"]] = results

    print(f"\n{'#'*60}")
    print(f"# SIMULATIONS TERMINÉES - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"{'#'*60}\n")

    return all_results


def save_results(results, output_file):
    """
    Sauvegarde les résultats en JSON.
    
    Args:
        results (dict): Résultats de toutes les simulations
        output_file (str): Chemin du fichier de sortie
    """
    # Convertir en format sérialisable JSON
    json_results = {}
    
    for scenario_name, scenario_data in results.items():
        json_results[scenario_name] = {
            "LEACH": {
                "metrics": scenario_data["LEACH"]["metrics"],
                "detailed_rounds": scenario_data["LEACH"]["detailed"][:20]  # Premiers 20 rounds
            },
            "LEACH-C": {
                "metrics": scenario_data["LEACH-C"]["metrics"],
                "detailed_rounds": scenario_data["LEACH-C"]["detailed"][:20]
            }
        }
    
    with open(output_file, 'w') as f:
        json.dump(json_results, f, indent=2)
    
    print(f"OK - Résultats sauvegardés: {output_file}")


if __name__ == "__main__":
    # Graine de randomisation pour reproductibilité
    random.seed(42)

    # Lancer les simulations DYNAMIQUES
    print("\n" + "="*70)
    print("PHASE 1: SIMULATIONS DYNAMIQUES (avec mobilité)")
    print("="*70)
    dynamic_results = run_all_scenarios(is_static=False)

    # Sauvegarder les résultats dynamiques
    save_results(dynamic_results, "/home/sorti/projects/AlgoRep/results/simulation_results_dynamic.json")

    # Lancer les simulations STATIQUES
    print("\n" + "="*70)
    print("PHASE 2: SIMULATIONS STATIQUES (sans mobilité)")
    print("="*70)
    random.seed(42)  # Réinitialiser la graine pour avoir les mêmes positions initiales
    static_results = run_all_scenarios(is_static=True)

    # Sauvegarder les résultats statiques
    save_results(static_results, "/home/sorti/projects/AlgoRep/results/simulation_results_static.json")

    # Afficher un résumé
    print("\n" + "="*70)
    print("RÉSUMÉ DES RÉSULTATS - DYNAMIQUE vs STATIQUE")
    print("="*70)

    for scenario_name in SCENARIOS[0:1]:  # Afficher un exemple
        scenario_label = scenario_name['name']
        print(f"\n{scenario_label}:")
        print("-" * 70)

        for protocol in ["LEACH", "LEACH-C"]:
            dynamic_metrics = dynamic_results[scenario_label][protocol]["metrics"]
            static_metrics = static_results[scenario_label][protocol]["metrics"]

            dyn_fdn = dynamic_metrics['first_dead_node_round'] or "N/A"
            stat_fdn = static_metrics['first_dead_node_round'] or "N/A"
            dyn_fmr = dynamic_metrics['first_muted_round'] or "N/A"
            stat_fmr = static_metrics['first_muted_round'] or "N/A"

            print(f"\n  {protocol}:")
            print(f"    Métrique              | Dynamique  | Statique   | Différence")
            if isinstance(dyn_fdn, int) and isinstance(stat_fdn, int):
                print(f"    FDN (First Dead Node) | {dyn_fdn:10} | {stat_fdn:10} | {stat_fdn - dyn_fdn:10.0f}")
            else:
                print(f"    FDN (First Dead Node) | {str(dyn_fdn):10} | {str(stat_fdn):10} | N/A")
            if isinstance(dyn_fmr, int) and isinstance(stat_fmr, int):
                print(f"    FMR (First Muted Rd)  | {dyn_fmr:10} | {stat_fmr:10} | {stat_fmr - dyn_fmr:10.0f}")
            else:
                print(f"    FMR (First Muted Rd)  | {str(dyn_fmr):10} | {str(stat_fmr):10} | N/A")
            print(f"    DLBI (Load Balance)   | {dynamic_metrics['dlbi']:10.4f} | {static_metrics['dlbi']:10.4f} | {static_metrics['dlbi'] - dynamic_metrics['dlbi']:10.4f}")
            print(f"    RSPI (Resilience)     | {dynamic_metrics['rspi']:10.4f} | {static_metrics['rspi']:10.4f} | {static_metrics['rspi'] - dynamic_metrics['rspi']:10.4f}")

    print(f"\n✓ Résultats dynamiques sauvegardés: /home/sorti/projects/AlgoRep/results/simulation_results_dynamic.json")
    print(f"✓ Résultats statiques sauvegardés: /home/sorti/projects/AlgoRep/results/simulation_results_static.json")
