import secrets
import socket
import struct
import sys


def read_line(conn):
    data = bytearray()
    while not data.endswith(b"\n"):
        part = conn.recv(1)
        if not part:
            raise ConnectionError("client disconnected")
        data.extend(part)
    return bytes(data[:-1])


def handle(conn):
    base = 0x555500000000 + (secrets.randbelow(0x10000) << 12)
    state = secrets.randbits(64)
    metadata = bytearray(struct.pack("<QQ", base + 0x1390, state))
    cards = [bytearray(16) for _ in range(4)]
    conn.sendall(b"Index Card\n")
    while True:
        conn.sendall(b"> ")
        parts = read_line(conn).split()
        if not parts:
            conn.sendall(b"empty command\n")
            continue
        if parts[0] == b"run":
            callback, preserved = struct.unpack("<QQ", metadata)
            good = callback == base + 0x11d0 and preserved == state
            conn.sendall(b"flag{index_card_training}\n" if good else b"nothing happened\n")
            return
        if len(parts) < 2 or parts[0] not in (b"read", b"write"):
            conn.sendall(b"bad command\n")
            continue
        try:
            index = int(parts[1])
        except ValueError:
            conn.sendall(b"bad index\n")
            continue
        if index >= len(cards) or index < -1:
            conn.sendall(b"out of range\n")
            continue
        slot = metadata if index == -1 else cards[index]
        if parts[0] == b"read":
            conn.sendall(b"card=" + bytes(slot).hex().encode() + b"\n")
        elif len(parts) == 3:
            try:
                data = bytes.fromhex(parts[2].decode())
            except ValueError:
                data = b""
            if len(data) == 16:
                slot[:] = data
                conn.sendall(b"saved\n")
            else:
                conn.sendall(b"need 16 bytes\n")
        else:
            conn.sendall(b"bad command\n")


def serve(host, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        listener.bind((host, port))
        listener.listen()
        print("listening on", listener.getsockname(), flush=True)
        while True:
            conn, _ = listener.accept()
            with conn:
                try:
                    handle(conn)
                except ConnectionError:
                    pass


if __name__ == "__main__":
    host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 31342
    serve(host, port)
