"""
Record a video of the perspective illusion animation.
Uses Playwright to open the auto-animating HTML, record frames, then encode with ffmpeg.
"""
import asyncio, subprocess, os, tempfile
from playwright.async_api import async_playwright

HTML = """\
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
  * { margin:0; padding:0; box-sizing:border-box; }
  body { background:#f8f6f1; display:flex; flex-direction:column;
         align-items:center; justify-content:center;
         width:820px; height:480px; overflow:hidden; }
  canvas { display:block; }
  .label { margin-top:14px; font-family:Georgia,serif;
           font-size:13px; color:#aaa; letter-spacing:0.1em; }
</style>
</head>
<body>
<canvas id="c" width="820" height="400"></canvas>
<p class="label">drag the figure · 透視図法</p>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const W = 820, H = 400;

const VP     = { x: W*0.04, y: H*0.28 };
const groundY = H*0.72;
const xMin   = W*0.12;
const xMax   = W*0.88;
const BASE_H = H*0.52;

function scaleAt(x) {
  const n = (x - xMin) / (xMax - xMin);
  return 0.12 + n * 0.88;
}

function drawFigure(x, gY, scale, alpha, isMain) {
  const h=BASE_H*scale, top=gY-h, cx=x;
  const hr=h*0.115, sw=h*0.22, hw=h*0.16;
  const headCY=top+hr*1.1, neck=top+hr*2.4, waist=top+h*0.52;
  const hipL=cx-hw/2, hipR=cx+hw/2;
  const kneeL=cx-hw*0.35, kneeR=cx+hw*0.35;
  const ankleL=cx-hw*0.25, ankleR=cx+hw*0.25;
  const footL=ankleL-h*0.06, footR=ankleR+h*0.06;
  ctx.save();
  ctx.globalAlpha=alpha;
  ctx.strokeStyle=isMain?'#1a1a1a':'#555';
  ctx.fillStyle=isMain?'#f8f6f1':'#e8e5de';
  ctx.lineWidth=isMain?Math.max(1.5,2.2*scale):Math.max(0.8,1.4*scale);
  ctx.lineCap='round'; ctx.lineJoin='round';
  ctx.beginPath(); ctx.arc(cx,headCY,hr,0,Math.PI*2); ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(cx-sw/2,neck); ctx.lineTo(cx+sw/2,neck);
  ctx.lineTo(hipR,waist); ctx.lineTo(hipR-hw*0.1,waist+h*0.03);
  ctx.lineTo(hipL+hw*0.1,waist+h*0.03); ctx.lineTo(hipL,waist);
  ctx.closePath(); ctx.fill(); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(hipL,waist); ctx.lineTo(kneeL,waist+h*0.26);
  ctx.lineTo(ankleL,gY-h*0.01); ctx.lineTo(footL,gY); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(hipR,waist); ctx.lineTo(kneeR,waist+h*0.26);
  ctx.lineTo(ankleR,gY-h*0.01); ctx.lineTo(footR,gY); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(cx-sw/2,neck+h*0.04);
  ctx.lineTo(cx-sw/2-h*0.08,neck+h*0.22); ctx.lineTo(cx-sw/2-h*0.04,neck+h*0.39); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(cx+sw/2,neck+h*0.04);
  ctx.lineTo(cx+sw/2+h*0.08,neck+h*0.22); ctx.lineTo(cx+sw/2+h*0.04,neck+h*0.39); ctx.stroke();
  ctx.restore();
}

let t = 0;
let frame = 0;
const TOTAL_FRAMES = 180; // 3s @ 60fps

function draw(t) {
  ctx.clearRect(0,0,W,H);
  // Perspective lines
  for(let i=0;i<=6;i++){
    const gx=xMin+(xMax-xMin)*(i/6);
    ctx.beginPath(); ctx.moveTo(VP.x,VP.y);
    ctx.lineTo(gx+(gx-VP.x)*0.3, groundY+(groundY-VP.y)*0.3);
    ctx.strokeStyle='rgba(180,170,155,0.35)'; ctx.lineWidth=0.8; ctx.stroke();
  }
  ctx.beginPath(); ctx.moveTo(0,VP.y); ctx.lineTo(W,VP.y);
  ctx.strokeStyle='rgba(180,170,155,0.5)'; ctx.lineWidth=0.8;
  ctx.setLineDash([6,8]); ctx.stroke(); ctx.setLineDash([]);
  ctx.beginPath(); ctx.moveTo(xMin-20,groundY); ctx.lineTo(xMax+20,groundY);
  ctx.strokeStyle='#c0392b'; ctx.lineWidth=2; ctx.stroke();
  const ghosts=[0.15,0.38,0.62];
  const fx=xMin+(xMax-xMin)*t;
  for(const g of ghosts){
    const gx=xMin+(xMax-xMin)*g;
    if(Math.abs(gx-fx)>BASE_H*0.15) drawFigure(gx,groundY,scaleAt(gx),0.22,false);
  }
  drawFigure(fx,groundY,scaleAt(fx),1,true);
  // Dot
  ctx.beginPath(); ctx.arc(fx,groundY,9,0,Math.PI*2);
  ctx.fillStyle='#2980b9'; ctx.fill();
  ctx.strokeStyle='#fff'; ctx.lineWidth=2; ctx.stroke();
  ctx.beginPath(); ctx.arc(fx,groundY,13,0,Math.PI*2);
  ctx.strokeStyle='rgba(41,128,185,0.35)'; ctx.lineWidth=1.5; ctx.stroke();
  ctx.beginPath(); ctx.arc(VP.x,VP.y,4,0,Math.PI*2);
  ctx.fillStyle='rgba(180,170,155,0.7)'; ctx.fill();
}

// Animate: smooth pendulum (ease in/out)
function easeInOut(x) {
  return x < 0.5 ? 2*x*x : 1-Math.pow(-2*x+2,2)/2;
}

let startTime = null;
const DURATION = 4000; // 4s per sweep

function animate(ts) {
  if (!startTime) startTime = ts;
  const elapsed = (ts - startTime) % (DURATION * 2);
  const phase = elapsed / DURATION;
  // 0→1 going right, 1→2 going left
  const raw = phase < 1 ? phase : 2 - phase;
  t = 0.05 + easeInOut(raw) * 0.9;
  draw(t);
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
</script>
</body>
</html>
"""

OUTDIR  = "/Users/bowang/.openclaw/workspace"
FRAMES  = os.path.join(OUTDIR, "_frames")
OUTFILE = os.path.join(OUTDIR, "perspective-illusion-demo.mp4")
os.makedirs(FRAMES, exist_ok=True)

FPS      = 30
DURATION = 8   # seconds
N_FRAMES = FPS * DURATION

async def main():
    html_path = os.path.join(OUTDIR, "_persp_anim.html")
    with open(html_path, "w") as f:
        f.write(HTML)

    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page    = await browser.new_page(viewport={"width": 820, "height": 480})
        await page.goto(f"file://{html_path}")
        await asyncio.sleep(0.3)  # let it settle

        print(f"Capturing {N_FRAMES} frames at {FPS}fps ({DURATION}s)...")
        for i in range(N_FRAMES):
            await page.screenshot(path=os.path.join(FRAMES, f"frame_{i:04d}.png"), full_page=False)
            # Advance time by 1/fps via JS
            await page.evaluate(f"window.performance && document.dispatchEvent(new Event('_tick'))")
            await asyncio.sleep(1 / FPS)
            if i % 30 == 0:
                print(f"  {i}/{N_FRAMES}")

        await browser.close()

    print("Encoding video with ffmpeg...")
    subprocess.run([
        "ffmpeg", "-y",
        "-framerate", str(FPS),
        "-i", os.path.join(FRAMES, "frame_%04d.png"),
        "-vf", "scale=820:480",
        "-c:v", "libx264",
        "-pix_fmt", "yuv420p",
        "-crf", "18",
        "-preset", "fast",
        OUTFILE
    ], check=True)

    # Cleanup frames
    import shutil
    shutil.rmtree(FRAMES)
    os.remove(html_path)
    print(f"Done! → {OUTFILE}")

asyncio.run(main())
