#!/usr/bin/env python3
"""Tira de quadros de um trecho curto do vídeo, com o tempo carimbado em cada quadro.

Serve para ver de perto o que a folha de 1 quadro por segundo não mostra: como a palavra da legenda
entra, quanto dura um zoom, em que quadro um título some.

Uso (<SKILL> é a pasta desta skill):
    uv run --directory "<SKILL>" python helpers/tira_de_quadros.py VIDEO INICIO FIM --saida tira.jpg
        [--fps 10] [--colunas 6] [--largura 240] [--faixa 0.6:0.85]

INICIO e FIM em segundos (ex.: 12.5) ou mm:ss.cc (ex.: 00:12.50).
--faixa corta só uma parte da altura (frações de 0 a 1, de cima para baixo), por exemplo a faixa da legenda.
"""

from __future__ import annotations

import argparse
import re
import subprocess
import sys
import tempfile
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

SKILL = Path(__file__).resolve().parent.parent   # esta skill (helpers/..)


def tempo(txt: str) -> float:
    txt = txt.strip().replace(",", ".")
    if ":" in txt:
        m, s = txt.split(":", 1)
        return int(m) * 60 + float(s)
    return float(txt)


def fonte(tam: int):
    for p in (SKILL / "assets" / "fonts" / "figtree" / "Figtree-Variable.ttf",
              Path("/System/Library/Fonts/Menlo.ttc"), Path("C:/Windows/Fonts/arial.ttf")):
        try:
            return ImageFont.truetype(str(p), tam)
        except Exception:
            continue
    return ImageFont.load_default()


def main() -> int:
    ap = argparse.ArgumentParser(description="Tira de quadros de um trecho do vídeo, com o tempo em cada quadro.")
    ap.add_argument("video", type=Path)
    ap.add_argument("inicio")
    ap.add_argument("fim")
    ap.add_argument("--saida", type=Path, required=True, help="arquivo .jpg de saída")
    ap.add_argument("--fps", type=float, default=10.0, help="quadros por segundo na tira (padrão 10; use 30 para ver quadro a quadro)")
    ap.add_argument("--colunas", type=int, default=6)
    ap.add_argument("--largura", type=int, default=240, help="largura de cada quadro na tira, em px")
    ap.add_argument("--faixa", help="parte da altura, ex.: 0.6:0.85")
    a = ap.parse_args()
    ini, fim = tempo(a.inicio), tempo(a.fim)
    if fim <= ini:
        print("erro: o FIM tem que ser depois do INICIO", file=sys.stderr)
        return 2
    if (fim - ini) * a.fps > 240:
        print("erro: quadros demais para uma tira (mais de 240). Encurte o trecho ou baixe o --fps.", file=sys.stderr)
        return 2
    video = a.video.expanduser().absolute()
    if not video.is_file():
        print(f"erro: não achei {video}", file=sys.stderr)
        return 2
    with tempfile.TemporaryDirectory(prefix="sc-tira-") as tmp:
        r = subprocess.run(["ffmpeg", "-hide_banner", "-nostdin", "-nostats", "-loglevel", "info", "-ss", f"{ini:.3f}",
                            "-i", str(video), "-t", f"{fim - ini:.3f}", "-an", "-vf", f"fps={a.fps},showinfo",
                            "-q:v", "3", str(Path(tmp) / "t_%04d.jpg")], capture_output=True)
        if r.returncode != 0:
            print("erro: o ffmpeg falhou: " + r.stderr.decode("utf-8", "replace")[-400:], file=sys.stderr)
            return 1
        rel = [float(m.group(1)) for m in re.finditer(r"\bn:\s*\d+\s+pts:\s*-?\d+\s+pts_time:(-?[\d.]+)",
                                                      r.stderr.decode("utf-8", "replace"))]
        arqs = sorted(Path(tmp).glob("t_*.jpg"))
        if not arqs:
            print("erro: nenhum quadro nesse trecho", file=sys.stderr)
            return 1
        if len(rel) != len(arqs):
            rel = [i / a.fps for i in range(len(arqs))]
        f = fonte(15)
        tiles = []
        for t_rel, arq in zip(rel, arqs):
            im = Image.open(arq).convert("RGB")
            if a.faixa:
                y0, y1 = [float(x) for x in a.faixa.split(":")]
                im = im.crop((0, int(y0 * im.height), im.width, int(y1 * im.height)))
            h = max(1, int(round(im.height * a.largura / im.width)))
            im = im.resize((a.largura, h))
            d = ImageDraw.Draw(im)
            t = ini + t_rel
            rot = f"{int(t // 60):02d}:{t % 60:05.2f}"
            caixa = d.textbbox((4, 2), rot, font=f)
            d.rectangle([0, 0, caixa[2] + 4, caixa[3] + 3], fill=(0, 0, 0))
            d.text((4, 2), rot, fill=(255, 230, 0), font=f)
            tiles.append(im)
    th = tiles[0].height
    lin = (len(tiles) + a.colunas - 1) // a.colunas
    folha = Image.new("RGB", (a.largura * a.colunas, th * lin), (18, 18, 18))
    for i, im in enumerate(tiles):
        folha.paste(im, ((i % a.colunas) * a.largura, (i // a.colunas) * th))
    saida = a.saida.expanduser().absolute()
    saida.parent.mkdir(parents=True, exist_ok=True)
    folha.save(saida, quality=90)
    print(f"Tira pronta: {saida} ({len(tiles)} quadros, {a.fps:g} por segundo)")
    return 0


if __name__ == "__main__":
    sys.exit(main())
