# Importer le module socket pour la communication réseau
import socket
# Importer le module sys pour accéder aux arguments en ligne de commande
import sys

# Récupérer la règle BMR, le préfixe du routeur, les EA bits length et l'offset comme arguments en ligne de commande
# Le format attendu est: python script.py rule_ipv6_prefix/length ipv4_prefix/length router_prefix/length ea_bits_length offset
# Par exemple: python script.py 2001:db8:ce::/51 192.0.2.0/24 2001:db8::/32 11 6
if len(sys.argv) != 6:
    print("Usage: python script.py rule_ipv6_prefix/length ipv4_prefix/length router_prefix/length ea_bits_length offset")
    sys.exit(1)

bmr_ipv6_prefix = sys.argv[1]
bmr_ipv4_prefix = sys.argv[2]
router_prefix = sys.argv[3]
ea_bits_length = int(sys.argv[4])
offset = int(sys.argv[5])

# Calculer les variables nécessaires pour le calcul des ports
a = offset # Nombre de bits pour le bloc de ports
q = ea_bits_length - a # Nombre de bits pour le PSID
m = 16 - a # Nombre de bits pour l'index de ports
o = ea_bits_length # Nombre de bits pour les EA-bits
p = 32 - (128 - o - 96) # Nombre de bits pour le suffixe d'adresse IPv4
r = 32 - p # Longueur du préfixe IPv4 réservé aux CE
s = 2 ** q # Nombre de sets de ports par adresse IPv4
t = 2 ** m # Nombre de ports par set

# Définir la règle de mappage de base (BMR)
bmr = {
    "rule_ipv6_prefix": bmr_ipv6_prefix, # Préfixe IPv6 de la règle
    "ipv4_prefix": bmr_ipv4_prefix, # Préfixe IPv4 réservé aux CE
    "ea_bits_length": o # Longueur des EA-bits
}

# Définir une fonction pour convertir une adresse IPv4 en binaire
def ipv4_to_bin(ipv4):
    # Séparer les octets de l'adresse
    octets = ipv4.split(".")
    # Convertir chaque octet en binaire sur 8 bits
    bin_octets = [format(int(octet), "08b") for octet in octets]
    # Concaténer les octets binaires
    bin_ipv4 = "".join(bin_octets)
    # Retourner l'adresse binaire
    return bin_ipv4

# Définir une fonction pour convertir une adresse binaire en IPv4
def bin_to_ipv4(bin_ipv4):
    # Séparer les octets binaires
    bin_octets = [bin_ipv4[i:i+8] for i in range(0, len(bin_ipv4), 8)]
    # Convertir chaque octet binaire en décimal
    octets = [str(int(bin_octet, 2)) for bin_octet in bin_octets]
    # Concaténer les octets décimaux avec des points
    ipv4 = ".".join(octets)
    # Retourner l'adresse IPv4
    return ipv4

# Définir une fonction pour calculer le préfixe IPv6 de l'utilisateur final (End-user IPv6 Prefix)
def calculate_euip(rule_ipv6_prefix, ipv4_address, psid):
    # Extraire la longueur du préfixe IPv6 de la règle
    rule_prefix, rule_length = rule_ipv6_prefix.split("/")
    rule_length = int(rule_length)
    # Convertir le préfixe IPv6 de la règle en binaire
    rule_prefix_bin = bin(int(socket.inet_pton(socket.AF_INET6, rule_prefix).hex(), 16))[2:].zfill(128)
    # Extraire le suffixe de l'adresse IPv4
    ipv4_suffix = ipv4_to_bin(ipv4_address)[32-p:]
    # Convertir le PSID en binaire
    psid_bin = format(psid, "0" + str(q) + "b")
    # Concaténer les EA-bits
    ea_bits = ipv4_suffix + psid_bin
    # Concaténer le préfixe IPv6 de la règle et les EA-bits
    euip_bin = rule_prefix_bin[:rule_length] + ea_bits
    # Convertir le préfixe IPv6 de l'utilisateur final en hexadécimal
    euip_hex = format(int(euip_bin, 2), "032x")
    # Séparer le préfixe en groupes de 4 chiffres
    euip_groups = [euip_hex[i:i+4] for i in range(0, len(euip_hex), 4)]
    # Concaténer les groupes avec des deux-points
    euip = ":".join(euip_groups) + "/" + str(rule_length + o)
    # Retourner le préfixe IPv6 de l'utilisateur final
    return euip

# Définir une fonction pour calculer le nombre de ports disponibles pour un CE
def calculate_available_ports(rule_ipv4_prefix, ipv4_address, psid):
    # Extraire la longueur du préfixe IPv4 de la règle
    rule_prefix, rule_length = rule_ipv4_prefix.split("/")
    rule_length = int(rule_length)
    # Convertir le préfixe IPv4 de la règle en binaire
    rule_prefix_bin = ipv4_to_bin(rule_prefix)
    # Extraire le suffixe de l'adresse IPv4
    ipv4_suffix = ipv4_to_bin(ipv4_address)[32-p:]
    # Vérifier si l'adresse IPv4 appartient au préfixe IPv4 de la règle
    if rule_prefix_bin == ipv4_suffix[:r]:
        # Calculer le nombre de ports disponibles
        available_ports = ((2 ** a - 1) * 2 ** m)
        # Retourner le nombre de ports disponibles
        return available_ports
    else:
        # Retourner zéro
        return 0

# Définir une fonction pour calculer le PSID à partir de la BMR et de l'adresse IPv4
def calculate_psid(bmr_ipv4_prefix, ipv4_address):
    # Extraire la longueur du préfixe IPv4 de la BMR
    bmr_prefix, bmr_length = bmr_ipv4_prefix.split("/")
    bmr_length = int(bmr_length)
    # Convertir le préfixe IPv4 de la BMR en binaire
    bmr_prefix_bin = ipv4_to_bin(bmr_prefix)
    # Extraire le suffixe de l'adresse IPv4
    ipv4_suffix = ipv4_to_bin(ipv4_address)[32-p:]
    # Calculer le PSID en utilisant la formule
    psid = (int(ipv4_suffix, 2) ^ int(bmr_prefix_bin, 2)) // (2 ** a)
    # Retourner le PSID
    return psid

 # Définir une fonction pour calculer l'adresse IPv4 publique à partir du préfixe du routeur et du préfixe IPv6 de l'utilisateur final
def calculate_public_ipv4(router_prefix, euip):
    # Extraire la longueur du préfixe du routeur
    router_prefix, router_length = router_prefix.split("/")
    router_length = int(router_length)
    # Convertir le préfixe du routeur en binaire
    router_prefix_bin = bin(int(socket.inet_pton(socket.AF_INET6, router_prefix).hex(), 16))[2:].zfill(128)
    # Extraire le préfixe IPv6 de l'utilisateur final
    euip_prefix, euip_length = euip.split("/")
    euip_length = int(euip_length)
    # Convertir le préfixe IPv6 de l'utilisateur final en binaire
    euip_prefix_bin = bin(int(socket.inet_pton(socket.AF_INET6, euip_prefix).hex(), 16))[2:].zfill(128)
    # Vérifier si le préfixe IPv6 de l'utilisateur final appartient au préfixe du routeur
    if router_prefix_bin == euip_prefix_bin[:router_length]:
        # Extraire les EA-bits du préfixe IPv6 de l'utilisateur final
        ea_bits = euip_prefix_bin[router_length:euip_length]
        # Extraire le suffixe de l'adresse IPv4 des EA-bits
        ipv4_suffix = ea_bits[:p]
        # Calculer le PSID à partir du suffixe de l'adresse IPv4
        psid = int(ea_bits[p:], 2)
        # Calculer l'adresse IPv4 publique en utilisant la formule
        public_ipv4 = int(ipv4_suffix, 2) ^ (psid * (2 ** a))
        # Convertir l'adresse IPv4 publique en décimal
        public_ipv4_dec = bin_to_ipv4(format(public_ipv4, "0" + str(p) + "b"))
        # Retourner l'adresse IPv4 publique
        return public_ipv4_dec
    else:
        # Retourner une erreur
        return "L'adresse IPv6 ne correspond pas au préfixe du routeur"

# Définir les variables ipv4_address et psid en utilisant les fonctions calculate_public_ipv4 et calculate_psid
ipv4_address = calculate_public_ipv4
psid = calculate_psid

# Afficher les résultats du calcul des ports disponibles, du préfixe IPv6 de l'utilisateur final et de l'adresse IPv4 publique
print("Ports disponibles :", calculate_available_ports(bmr["ipv4_prefix"], ipv4_address, psid))
print("Préfixe IPv6 de l'utilisateur final :", calculate_euip(bmr["rule_ipv6_prefix"], ipv4_address, psid))
print("Adresse IPv4 publique :", ipv4_address)
