#!/usr/bin/env python3
"""cwnd de Reno, una fila por RTT, con las reglas del libro y del RFC 5681.

Es un modelo para hacer la cuenta en papel: cuenta en paquetes y por RTT. No tiene ACK, ni
temporizadores, ni datos; no es un transporte.

    python3 reno_trace.py --ssthresh 8 --rounds 12 --dup-at-cwnd 12
    python3 reno_trace.py --ssthresh 16 --rounds 26 --dup-at-round 9 --timeout-at-round 17
"""
import argparse


def trace(rounds, ssthresh, initial=1, dup_rounds=(), timeout_rounds=(), dup_at_cwnd=None):
    """Devuelve filas (ronda, cwnd, ssthresh, fase, evento al final de esa ronda)."""
    rows = []
    cwnd = initial
    fired = False
    for number in range(1, rounds + 1):
        phase = "slow start" if cwnd < ssthresh else "congestion avoidance"
        dup = number in dup_rounds
        if dup_at_cwnd is not None and cwnd == dup_at_cwnd and not fired:
            dup, fired = True, True
        timeout = number in timeout_rounds
        event = "3 ACK dup." if dup else ("timeout" if timeout else "")
        rows.append((number, cwnd, ssthresh, phase, event))
        if dup or timeout:
            ssthresh = max(cwnd // 2, 2)
            cwnd = ssthresh if dup else 1
        elif cwnd < ssthresh:
            cwnd = min(cwnd * 2, ssthresh)
        else:
            cwnd += 1
    return rows


def main():
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--rounds", type=int, default=20)
    parser.add_argument("--ssthresh", type=int, default=8)
    parser.add_argument("--initial", type=int, default=1)
    parser.add_argument("--dup-at-round", type=int, action="append", default=[])
    parser.add_argument("--timeout-at-round", type=int, action="append", default=[])
    parser.add_argument("--dup-at-cwnd", type=int, default=None)
    args = parser.parse_args()
    print(f"{'RTT':>3} {'cwnd':>5} {'ssthresh':>8}  {'fase':<21} evento")
    for number, cwnd, ssthresh, phase, event in trace(
            args.rounds, args.ssthresh, args.initial, args.dup_at_round,
            args.timeout_at_round, args.dup_at_cwnd):
        print(f"{number:>3} {cwnd:>5} {ssthresh:>8}  {phase:<21} {event}".rstrip())


if __name__ == "__main__":
    main()
