import sys
import io
import contextlib
import re

# Get the P4 pipeline object
p4 = bfrt.basic.pipe

# Capture bfrt.port.port.dump(from_hw=True) output
with io.StringIO() as buf, contextlib.redirect_stdout(buf):
    bfrt.port.port.dump(from_hw=True)
    port_dump_output = buf.getvalue()

# Map front-panel port names, e.g. "1/0", to device ports, e.g. 132
port_mapping = {}

dev_port_pattern = re.compile(r"\$DEV_PORT\s+:\s+(0x[0-9A-Fa-f]+)")
port_name_pattern = re.compile(r"\$PORT_NAME\s+:\s+(\S+)")

current_dev_port = None

for line in port_dump_output.split("\n"):
    dev_port_match = dev_port_pattern.search(line)
    if dev_port_match:
        current_dev_port = int(dev_port_match.group(1), 16)

    port_name_match = port_name_pattern.search(line)
    if port_name_match and current_dev_port is not None:
        port_name = port_name_match.group(1)
        port_mapping[port_name] = current_dev_port
        current_dev_port = None

print(f"Port Mapping: {port_mapping}")


def main(pairs):
    forwarding = p4.Ingress.forwarding
    forwarding.clear()
    for a_logical, b_logical in pairs:
        a_dev = port_mapping.get(f"{a_logical}/0")
        b_dev = port_mapping.get(f"{b_logical}/0")

        if a_dev is None or b_dev is None:
            print(f"Error: missing mapping for {a_logical}/0 or {b_logical}/0")
            return

        # Packets entering port A exit port B.
        forwarding.add_with_send_using_port(
            ingress_port=a_dev,
            port=b_dev
        )

        # Packets entering port B exit port A.
        forwarding.add_with_send_using_port(
            ingress_port=b_dev,
            port=a_dev
        )

        print(f"Programmed: {a_logical}/0 ({a_dev}) <-> {b_logical}/0 ({b_dev})")

    bfrt.complete_operations()

    print("\n******************* PROGRAMMING RESULTS *****************")
    print("Table forwarding:")
    forwarding.dump(table=True)


if __name__ == "__main__":
    pairs = [(1, 5), (2, 6), (3, 7), (4, 8)]
    main(pairs)