#!/usr/bin/env python3
"""ft-eyes-replay: play a recording into the shared-frame layout, as ft-eyegrab --share
would, at the recorded pace, to test ft-eyes without the headset.

Usage: lab/py lab/ft-eyes-replay NAME [PATH] [--from S] [--for S]
NAME is a recording (a bare name is in eyes_lab.CAPTURES). PATH defaults to
/dev/shm/frametop-eyes-cams-replay; run ft-eyes with FT_EYES_CAMS=PATH (and FT_EYES_GAZE=...
so it doesn't overwrite the live output).
"""
import mmap
import os
import struct
import sys
import time
from pathlib import Path

import numpy as np

sys.path.insert(0, str(Path(__file__).resolve().parent))
from eyes_lab import capture  # noqa: E402

W, H, SLOTS = 512, 400, 8
ESIZE = 64 + W * H


def main(argv):
    cap = capture(argv[0])
    path = argv[1] if len(argv) > 1 and not argv[1].startswith("--") else "/dev/shm/frametop-eyes-cams-replay"
    start = float(argv[argv.index("--from") + 1]) if "--from" in argv else 0.0
    length = float(argv[argv.index("--for") + 1]) if "--for" in argv else 1e9
    idx = np.array([[float(v) for v in l.split()] for l in (cap / "index.txt").read_text().splitlines()
                    if len(l.split()) == 4])
    frames = np.memmap(cap / "frames.raw", dtype=np.uint8, mode="r").reshape(-1, H, W)
    size = 64 + 2 * SLOTS * ESIZE
    if os.path.exists(path):
        os.unlink(path)
    fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
    os.ftruncate(fd, size)
    mm = mmap.mmap(fd, size)
    os.close(fd)
    struct.pack_into("<6I2QI", mm, 0, 0x31434546, 1, W, H, SLOTS, ESIZE, 0, 0, os.getpid())
    count = [0, 0]
    t0 = idx[0, 3] + start
    wall0 = time.monotonic()
    try:
        for i in range(len(frames)):
            t = idx[i, 3]
            if t < t0:
                continue
            if t - t0 > length:
                break
            delay = (t - t0) - (time.monotonic() - wall0)
            if delay > 0:
                time.sleep(delay)
            cam = int(idx[i, 2])
            n = count[cam]
            off = 64 + (cam * SLOTS + n % SLOTS) * ESIZE
            seq = struct.unpack_from("<Q", mm, off)[0]
            struct.pack_into("<Q", mm, off, seq + 1)
            mm[off + 64:off + 64 + W * H] = frames[i].tobytes()
            struct.pack_into("<dQII", mm, off + 8, t, n, cam, int(idx[i, 1]))
            struct.pack_into("<Q", mm, off, seq + 2)
            count[cam] = n + 1
            struct.pack_into("<Q", mm, 24 + 8 * cam, n + 1)
    finally:
        os.unlink(path)
    print(f"replayed {sum(count)} frames")


if __name__ == "__main__":
    main(sys.argv[1:])
