#!/usr/bin/env python3
"""
Script di test per verificare se il server SFP sta inviando pacchetti
e per analizzare il contenuto dei messaggi ricevuti.
"""

import socket
import time
import sys
import ctypes
from datetime import datetime


# Struttura SFP Header (32 bytes)
class SFPHeader(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("SFP_MARKER", ctypes.c_uint8),
        ("SFP_DIRECTION", ctypes.c_uint8),
        ("SFP_PROT_VER", ctypes.c_uint8),
        ("SFP_PT_SPARE", ctypes.c_uint8),
        ("SFP_TAG", ctypes.c_uint8),
        ("SFP_SRC", ctypes.c_uint8),
        ("SFP_FLOW", ctypes.c_uint8),
        ("SFP_TID", ctypes.c_uint8),
        ("SFP_FLAGS", ctypes.c_uint8),
        ("SFP_WIN", ctypes.c_uint8),
        ("SFP_ERR", ctypes.c_uint8),
        ("SFP_ERR_INFO", ctypes.c_uint8),
        ("SFP_TOTFRGAS", ctypes.c_uint16),
        ("SFP_FRAG", ctypes.c_uint16),
        ("SFP_RECTYPE", ctypes.c_uint8),
        ("SFP_RECSPARE", ctypes.c_uint8),
        ("SFP_PLDAP", ctypes.c_uint8),
        ("SFP_PLEXT", ctypes.c_uint8),
        ("SFP_RECCOUNTER", ctypes.c_uint16),
        ("SFP_PLSIZE", ctypes.c_uint16),
        ("SFP_TOTSIZE", ctypes.c_uint32),
        ("SFP_PLOFFSET", ctypes.c_uint32),
    ]


# DataTag (8 bytes)
class DataTag(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("ID", ctypes.c_uint8 * 2),
        ("VALID", ctypes.c_uint8),
        ("VERSION", ctypes.c_uint8),
        ("SIZE", ctypes.c_uint32),
    ]


def main():
    # Configurazione
    LOCAL_IP = "0.0.0.0"
    LOCAL_PORT = 60012
    SERVER_IP = "127.0.0.1"
    SERVER_PORT = 60013

    print("=" * 70)
    print("TEST SERVER SFP - Verifica ricezione pacchetti")
    print("=" * 70)
    print(f"Locale:  {LOCAL_IP}:{LOCAL_PORT}")
    print(f"Server:  {SERVER_IP}:{SERVER_PORT}")
    print("=" * 70)

    # Crea socket UDP
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Aumenta il buffer di ricezione
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8388608)

        sock.bind((LOCAL_IP, LOCAL_PORT))
        sock.settimeout(5.0)  # Timeout di 5 secondi

        print(f"[OK] Socket creato e bound su {LOCAL_IP}:{LOCAL_PORT}")
        print(
            f"[INFO] Buffer size: {sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)} bytes"
        )
        print()

    except Exception as e:
        print(f"[ERROR] Impossibile creare socket: {e}")
        return 1

    print("In ascolto per pacchetti dal server...")
    print("(Premi Ctrl+C per terminare)")
    print()

    packet_count = 0
    start_time = time.time()
    last_report_time = start_time
    flow_stats = {}

    try:
        while True:
            try:
                data, addr = sock.recvfrom(65535)
                packet_count += 1
                current_time = time.time()

                # Analizza l'header SFP
                if len(data) < 32:
                    print(f"[WARN] Pacchetto troppo corto: {len(data)} bytes da {addr}")
                    continue

                try:
                    header = SFPHeader.from_buffer_copy(data)
                    flow_char = (
                        chr(header.SFP_FLOW)
                        if 32 <= header.SFP_FLOW <= 126
                        else f"0x{header.SFP_FLOW:02X}"
                    )
                    direction = (
                        chr(header.SFP_DIRECTION)
                        if 32 <= header.SFP_DIRECTION <= 126
                        else f"0x{header.SFP_DIRECTION:02X}"
                    )

                    # Aggiorna statistiche per flow
                    if flow_char not in flow_stats:
                        flow_stats[flow_char] = 0
                    flow_stats[flow_char] += 1

                    # Timestamp leggibile
                    ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]

                    # Analizza il payload (primi bytes dopo l'header)
                    payload = data[32:]
                    tag_info = ""
                    if len(payload) >= 8:
                        try:
                            tag = DataTag.from_buffer_copy(payload)
                            tag_str = (
                                f"{chr(tag.ID[0]) if 32 <= tag.ID[0] <= 126 else tag.ID[0]:02X}"
                                f"{chr(tag.ID[1]) if 32 <= tag.ID[1] <= 126 else tag.ID[1]:02X}"
                            )
                            tag_info = f" | Tag: {tag_str} (valid={tag.VALID}, size={tag.SIZE})"
                        except:
                            pass

                    # Mostra preview dei primi bytes del payload
                    preview = (
                        payload[:16].hex() if len(payload) >= 16 else payload.hex()
                    )

                    print(
                        f"[{ts}] Pkt #{packet_count:04d} | From: {addr[0]}:{addr[1]} | "
                        f"Flow: {flow_char} | Dir: {direction} | TID: {header.SFP_TID:03d} | "
                        f"Size: {len(data):5d} bytes | Payload: {header.SFP_PLSIZE:5d} bytes{tag_info}"
                    )

                    if packet_count <= 3:
                        print(f"       Payload preview: {preview}")
                        print()

                except Exception as e:
                    print(f"[ERROR] Impossibile parsare header: {e}")
                    print(f"       Raw bytes (primi 32): {data[:32].hex()}")

                # Report periodico ogni 5 secondi
                if current_time - last_report_time >= 5.0:
                    elapsed = current_time - start_time
                    rate = packet_count / elapsed if elapsed > 0 else 0
                    print()
                    print("-" * 70)
                    print(
                        f"[STATS] Tempo: {elapsed:.1f}s | Pacchetti: {packet_count} | "
                        f"Rate: {rate:.1f} pkt/s"
                    )
                    print(f"[STATS] Distribuzione per Flow: {flow_stats}")
                    print("-" * 70)
                    print()
                    last_report_time = current_time

            except socket.timeout:
                print(f"[WARN] Nessun pacchetto ricevuto negli ultimi 5 secondi")
                print(
                    f"       Verifica che il server sia in esecuzione su {SERVER_IP}:{SERVER_PORT}"
                )
                print()

    except KeyboardInterrupt:
        print()
        print("=" * 70)
        print("Test terminato dall'utente")

    finally:
        sock.close()
        elapsed = time.time() - start_time
        rate = packet_count / elapsed if elapsed > 0 else 0

        print("=" * 70)
        print("STATISTICHE FINALI:")
        print(f"  Durata:              {elapsed:.1f} secondi")
        print(f"  Pacchetti ricevuti:  {packet_count}")
        print(f"  Rate medio:          {rate:.1f} pkt/s")
        print(f"  Distribuzione Flow:  {flow_stats}")
        print("=" * 70)

        if packet_count == 0:
            print()
            print("[ATTENZIONE] Nessun pacchetto ricevuto!")
            print("Possibili cause:")
            print("  1. Il server non è in esecuzione")
            print("  2. Il server non sta trasmettendo sulla porta corretta")
            print("  3. Firewall sta bloccando i pacchetti")
            print("  4. Problema di network/routing")
            return 1

        return 0


if __name__ == "__main__":
    sys.exit(main())
