#!/usr/bin/env python3
# playSession.py - boot Modem Wars in VICE on a private Xvfb display and drive it with real key
# events (xdotool), taking screenshots through the remote monitor at each step.
#
#   python3 playSession.py <disk.d64> <outDir> <script>
# where <script> is a comma separated list of steps:
#   w<seconds>            wait
#   k<key>[:n]           send key n times (xdotool keysyms, e.g. KP_2, KP_0, F1, space)
#   s<name>              take a screenshot called <name>.png
import os, socket, subprocess, sys, time, random

SCRATCH = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRATCH)
from viceDriver import pickFreePort, recvUntilPrompt


def findDisplay():
    for n in range(90, 120):
        if not os.path.exists(f"/tmp/.X11-unix/X{n}"):
            return n
    raise SystemExit("no free X display")


def main():
    disk    = os.path.abspath(sys.argv[1])
    outDir  = os.path.abspath(sys.argv[2])
    steps   = sys.argv[3].split(",")
    os.makedirs(outDir, exist_ok=True)
    display = f":{findDisplay()}"
    port    = pickFreePort()
    env     = dict(os.environ, DISPLAY=display)

    xvfb = subprocess.Popen(["Xvfb", display, "-screen", "0", "800x600x24", "-nolisten", "tcp"],
                            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    time.sleep(2)
    vice = subprocess.Popen(
        ["x64sc", "-remotemonitor", "-remotemonitoraddress", f"127.0.0.1:{port}",
         "-drive8truedrive", "-drive8type", "1541", "-sounddev", "dummy", "-warp", "-jamaction", "2",
         "-autostart-warp", "-model", "ntsc", "-joydev2", "1", "-chdir", SCRATCH, "-autostart", disk],
        env=env, stdout=open(f"{outDir}/vice.log", "w"), stderr=subprocess.STDOUT)
    try:
        sock = None
        for _ in range(120):
            try:
                sock = socket.create_connection(("127.0.0.1", port), timeout=1)
                break
            except OSError:
                time.sleep(0.3)
        if sock is None:
            raise SystemExit("monitor did not open")
        recvUntilPrompt(sock, 10)
        sock.sendall(b"break 0800\n"); recvUntilPrompt(sock, 10)
        sock.sendall(b"x\n"); recvUntilPrompt(sock, 5)
        print("loading (warp) ...", flush=True)
        print(recvUntilPrompt(sock, 400)[-80:], flush=True)      # stops at $0800
        sock.sendall(b"del\n"); recvUntilPrompt(sock, 5)
        sock.sendall(b"x\n"); recvUntilPrompt(sock, 3)           # run at full speed from here

        # find the emulator window
        winId = None
        for _ in range(40):
            out = subprocess.run(["xdotool", "search", "--name", "VICE"], env=env,
                                 capture_output=True, text=True).stdout.split()
            if out:
                winId = out[-1]
                break
            time.sleep(0.5)
        print("window:", winId, flush=True)

        for step in steps:
            kind, rest = step[0], step[1:]
            if kind == "w":
                time.sleep(float(rest))
            elif kind == "k":
                key, _, count = rest.partition(":")
                for _ in range(int(count or 1)):
                    subprocess.run(["xdotool", "key", "--window", winId, "--clearmodifiers", key],
                                   env=env, check=False)
                    time.sleep(0.25)
            elif kind == "h":
                # hold a key down long enough for the game to sample it (it polls once per frame)
                key, _, ms = rest.partition(":")
                subprocess.run(["xdotool", "keydown", "--window", winId, key], env=env, check=False)
                time.sleep(int(ms or 300) / 1000.0)
                subprocess.run(["xdotool", "keyup", "--window", winId, key], env=env, check=False)
                time.sleep(0.3)
            elif kind == "s":
                sock.sendall(b"\n"); recvUntilPrompt(sock, 10)          # enter monitor
                path = f"{outDir}/{rest}.png"
                sock.sendall(f'screenshot "{path}" 2\n'.encode()); recvUntilPrompt(sock, 10)
                sock.sendall(b"x\n"); recvUntilPrompt(sock, 5)          # resume
                print("shot", path, flush=True)
        sock.sendall(b"\n"); recvUntilPrompt(sock, 5)
        sock.sendall(b"quit\n")
        time.sleep(1)
    finally:
        vice.kill(); vice.wait()
        xvfb.kill(); xvfb.wait()


main()
