"""
Classe Node : Représentation d'un nœud capteur dans le réseau WSN
"""

import math
import random
from config import (
    E_ELEC, E_FS, E_MP, D0, E_DA,
    FIELD_WIDTH, FIELD_HEIGHT, MAX_DISPLACEMENT_PER_ROUND, ENABLE_MOBILITY
)


class Node:
    """
    Représente un nœud (capteur) dans le réseau WSN.
    
    Attributes:
        node_id (int): Identifiant unique du nœud
        x, y (float): Position en mètres
        energy (float): Énergie résiduelle en Joules
        initial_energy (float): Énergie initiale (pour calcul %)
        is_alive (bool): État du nœud
        is_cluster_head (bool): Est-ce un CH?
        cluster_id (int): ID du cluster auquel le nœud appartient
        packets_to_send (int): Nombre de paquets en attente
    """
    
    def __init__(self, node_id, x, y, initial_energy):
        self.node_id = node_id
        self.x = x
        self.y = y
        self.energy = initial_energy
        self.initial_energy = initial_energy
        self.is_alive = True
        self.is_cluster_head = False
        self.cluster_id = None
        self.packets_to_send = 0
        self.total_packets_sent = 0
    
    def distance_to(self, other_x, other_y):
        """Calcule la distance euclidienne vers un point."""
        return math.sqrt((self.x - other_x) ** 2 + (self.y - other_y) ** 2)
    
    def consume_energy(self, energy_amount):
        """Consomme de l'énergie et met à jour l'état du nœud."""
        self.energy -= energy_amount
        if self.energy <= 0:
            self.energy = 0
            self.is_alive = False
    
    def transmit(self, data_bits, distance):
        """
        Calcule et consomme l'énergie de transmission.
        
        Args:
            data_bits (int): Nombre de bits à transmettre
            distance (float): Distance jusqu'au récepteur
        
        Returns:
            float: Énergie consommée
        """
        if distance <= D0:
            # Modèle espace libre
            energy_tx = E_ELEC * data_bits + E_FS * data_bits * (distance ** 2)
        else:
            # Modèle multi-trajet
            energy_tx = E_ELEC * data_bits + E_MP * data_bits * (distance ** 4)
        
        self.consume_energy(energy_tx)
        return energy_tx
    
    def receive(self, data_bits):
        """
        Calcule et consomme l'énergie de réception.
        
        Args:
            data_bits (int): Nombre de bits reçus
        
        Returns:
            float: Énergie consommée
        """
        energy_rx = E_ELEC * data_bits
        self.consume_energy(energy_rx)
        return energy_rx
    
    def aggregate(self, data_bits):
        """
        Calcule et consomme l'énergie d'agrégation de données.
        
        Args:
            data_bits (int): Nombre de bits agrégés
        
        Returns:
            float: Énergie consommée
        """
        energy_agg = E_DA * data_bits
        self.consume_energy(energy_agg)
        return energy_agg
    
    def move(self):
        """
        Met à jour la position du nœud avec un déplacement aléatoire.
        Déplacement max = MAX_DISPLACEMENT_PER_ROUND mètres.
        Reste dans les limites du champ.
        Only moves if ENABLE_MOBILITY is True (allows static network mode).
        """
        if not ENABLE_MOBILITY:
            return  # No movement in static mode

        angle = random.uniform(0, 2 * math.pi)
        distance = random.uniform(0, MAX_DISPLACEMENT_PER_ROUND)

        new_x = self.x + distance * math.cos(angle)
        new_y = self.y + distance * math.sin(angle)

        # Limiter aux bords du champ
        self.x = max(0, min(FIELD_WIDTH, new_x))
        self.y = max(0, min(FIELD_HEIGHT, new_y))
    
    def reset_for_round(self):
        """Reset les données de ronde (cluster, CH status, etc.)"""
        self.is_cluster_head = False
        self.cluster_id = None
        self.packets_to_send = 0
    
    def get_energy_percentage(self):
        """Retourne le pourcentage d'énergie restante."""
        if self.initial_energy == 0:
            return 0
        return (self.energy / self.initial_energy) * 100
    
    def __repr__(self):
        return f"Node({self.node_id}, pos=({self.x:.1f}, {self.y:.1f}), E={self.energy:.4f}J, alive={self.is_alive})"
