#!/usr/bin/env python3
"""ft-eyes-record: record a live session from the shared frames, so it can be replayed and
scored later (ft-eyes-score, ft-eyes-e2e), without root and alongside ft-eyes.

Reads /dev/shm/frametop-eyes-cams (ft-eyegrab, frametop-eyegrab.service) and writes every
frame of both cameras to a new recording NAME in eyes_lab.CAPTURES: frames.raw, index.txt
("<n> <slot> <camera> <time>", as ft-eyegrab --rec), and clocks.txt (wall clock and
CLOCK_MONOTONIC_RAW, read together). It touches /dev/shm/frametop-eyes-want every second, so
ft-eyegrab copies frames even without ft-eyes. About 2 GB a minute. Stops after SECONDS
(default 900), on Ctrl-C or SIGTERM, or when the disk gets below MIN_FREE_GB. The probe's
clicks are added later, on the Frame: `lab/py lab/ft-eyes-score --clicks NAME`.

Usage: lab/ft-eyes-record NAME [SECONDS]   (host Python is enough: no numpy)
"""
import shutil
import signal
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from eyes_lab import new_capture  # noqa: E402
import eyes_share  # noqa: E402  (gaze/tracker, on the path through eyes_lab)
from eyes_share import touch_want  # noqa: E402

MIN_FREE_GB = 20


def open_share(timeout):
    share = eyes_share.open_share(timeout)
    if share is None:
        sys.exit(f"ft-eyes-record: no {eyes_share.CAMS} (is frametop-eyegrab.service running? gaze/tracker/install.sh)")
    return share


def main(argv):
    if not argv or argv[0].startswith("-"):
        sys.exit(__doc__)
    secs = float(argv[1]) if len(argv) > 1 else 900.0
    out = new_capture(argv[0])
    stop = []
    for sig in (signal.SIGINT, signal.SIGTERM):
        signal.signal(sig, lambda *_: stop.append(1))
    share = open_share(10)
    (out / "clocks.txt").write_text(f"{time.time()} {time.clock_gettime(time.CLOCK_MONOTONIC_RAW)}\n")
    seen = [share.count(0), share.count(1)]
    written = dropped = 0
    end = time.monotonic() + secs
    check = touched = time.monotonic()
    with open(out / "frames.raw", "wb") as frames, open(out / "index.txt", "w") as index:
        while not stop and time.monotonic() < end:
            new = []
            for c in (0, 1):
                n = share.count(c)
                if n - seen[c] > share.slots:  # fell behind: those frames are gone
                    dropped += n - seen[c] - share.slots
                    seen[c] = n - share.slots
                for i in range(seen[c], n):
                    f = share.frame(c, i)
                    if f is None:
                        dropped += 1
                    else:
                        new.append((f[0], f[1], c, f[2]))
                seen[c] = n
            for t, slot, c, data in sorted(new, key=lambda f: f[0]):
                frames.write(data)
                index.write(f"{written} {slot} {c} {t:.6f}\n")
                written += 1
            if not new:
                time.sleep(0.002)
            if time.monotonic() - touched > 1:
                touched = time.monotonic()
                touch_want()
            if time.monotonic() - check > 5:
                check = time.monotonic()
                if shutil.disk_usage(out).free < MIN_FREE_GB * 1e9:
                    print(f"ft-eyes-record: under {MIN_FREE_GB} GB free, stopping", file=sys.stderr)
                    break
                if share.replaced():
                    print("ft-eyes-record: the frame share was restarted; following it", file=sys.stderr)
                    share = open_share(10)
                    seen = [share.count(0), share.count(1)]
    print(f"ft-eyes-record: {written} frames ({dropped} dropped) in {out}", file=sys.stderr)


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