"""Lee una captura con tshark y la devuelve como una lista de segmentos."""
import subprocess

FIELDS = {
    "frame": "frame.number",
    "time": "frame.time_relative",
    "src": "ip.src",
    "dst": "ip.dst",
    "sport": "tcp.srcport",
    "dport": "tcp.dstport",
    "seq": "tcp.seq",
    "seq_raw": "tcp.seq_raw",
    "ack": "tcp.ack",
    "len": "tcp.len",
    "flags": "tcp.flags.str",
    "win_field": "tcp.window_size_value",
    "win": "tcp.window_size",
    "mss": "tcp.options.mss_val",
    "wscale": "tcp.options.wscale.shift",
    "sack_perm": "tcp.options.sack_perm",
    "ts": "tcp.options.timestamp.tsval",
    "sack_le": "tcp.options.sack_le",
    "sack_re": "tcp.options.sack_re",
    "dup_ack": "tcp.analysis.duplicate_ack_num",
    "retrans": "tcp.analysis.retransmission",
    "fast": "tcp.analysis.fast_retransmission",
    "spurious": "tcp.analysis.spurious_retransmission",
    "out_of_order": "tcp.analysis.out_of_order",
    "zero_window": "tcp.analysis.zero_window",
    "probe": "tcp.analysis.keep_alive",
    "window_update": "tcp.analysis.window_update",
    "ack_rtt": "tcp.analysis.ack_rtt",
    "rto": "tcp.analysis.rto",
}
NUMBERS = {"frame", "sport", "dport", "seq", "seq_raw", "ack", "len", "win_field", "win", "mss",
           "wscale", "dup_ack"}
FLOATS = {"time", "ack_rtt", "rto"}
MARKS = {"sack_perm", "ts", "retrans", "fast", "spurious", "out_of_order", "zero_window", "probe",
         "window_update"}


class Segment:
    """Un segmento de la captura. Los campos de FIELDS se leen como atributos."""

    def __init__(self, values):
        for name, text in values.items():
            if name in MARKS:
                value = text != ""
            elif name in NUMBERS:
                value = int(text) if text else None
            elif name in FLOATS:
                value = float(text) if text else None
            else:
                value = text
            setattr(self, name, value)
        # tshark escribe las banderas con un carácter por bit: ··A·S· y similares.
        self.syn = "S" in self.flags
        self.has_ack = "A" in self.flags
        self.fin = "F" in self.flags
        self.rst = "R" in self.flags
        self.resent = self.retrans or self.fast or self.spurious


def read_capture(path):
    """Devuelve los segmentos TCP de la captura, en orden."""
    command = ["tshark", "-r", path, "-Y", "tcp", "-T", "fields", "-E", "separator=\t"]
    for field in FIELDS.values():
        command += ["-e", field]
    done = subprocess.run(command, capture_output=True, text=True, timeout=120)
    if done.returncode != 0:
        raise RuntimeError(done.stderr)
    segments = []
    for line in done.stdout.splitlines():
        segments.append(Segment(dict(zip(FIELDS, line.split("\t")))))
    return segments
