"""Calcula EstimatedRTT, DevRTT y el RTO para una lista de muestras, como el RFC 6298.

    python3 rto_table.py 100 90 120 95             muestras en ms; la primera inicializa
    python3 rto_table.py --start 100 5 120         parte de EstimatedRTT = 100 y DevRTT = 5
    python3 rto_table.py --pcap captures/x-a.pcap  toma las muestras de una captura

Solo hace la cuenta. No tiene temporizador ni retroceso: no es un emisor.
"""
import sys

from pcaptools import read_capture

ALPHA = 1 / 8
BETA = 1 / 4


def first_sample(sample):
    """Regla 2.2 del RFC 6298: la primera muestra inicializa los dos valores."""
    return sample, sample / 2


def next_sample(estimated, deviation, sample):
    """Regla 2.3 del RFC 6298. DevRTT se calcula antes y usa el EstimatedRTT anterior."""
    deviation = (1 - BETA) * deviation + BETA * abs(estimated - sample)
    estimated = (1 - ALPHA) * estimated + ALPHA * sample
    return estimated, deviation


def rto(estimated, deviation):
    """El RTO sin el piso de 1 s, para poder compararlo con las muestras."""
    return estimated + 4 * deviation


def table(samples, start=None):
    """Devuelve filas (muestra, EstimatedRTT, DevRTT, RTO)."""
    rows = []
    state = start
    for sample in samples:
        state = next_sample(*state, sample) if state else first_sample(sample)
        rows.append((sample, state[0], state[1], rto(*state)))
    return rows


def samples_from_capture(path):
    """Las muestras de RTT que Wireshark deduce al emparejar cada dato con su ACK, en ms."""
    segments = read_capture(path)
    client = (segments[0].src, segments[0].sport)
    return [s.ack_rtt * 1000 for s in segments
            if s.ack_rtt is not None and (s.src, s.sport) != client and not s.syn]


def main():
    args = sys.argv[1:]
    start = None
    if args[:1] == ["--pcap"] and len(args) == 2:
        samples = samples_from_capture(args[1])
        samples = samples[:: max(1, len(samples) // 12)]
    elif args[:1] == ["--start"] and len(args) >= 4:
        start = (float(args[1]), float(args[2]))
        samples = [float(x) for x in args[3:]]
    elif args and not args[0].startswith("-"):
        samples = [float(x) for x in args]
    else:
        print(__doc__)
        return 2
    print(f"{'muestra':>9} {'EstimatedRTT':>13} {'DevRTT':>8} {'RTO':>8}")
    for row in table(samples, start):
        print(f"{row[0]:9.2f} {row[1]:13.2f} {row[2]:8.2f} {row[3]:8.2f}")
    return 0


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