"""第148〜152話の冒頭の動き（2026-11-14）。話ごとに違う動き（143〜147・138〜142 とも重ねない）
148 来て止まり「積ミ込ミ／待チ」→ 左右をきょろきょろ（誰も来ない）→ 時計の針のように画面に「…」が増え、ゆっくり沈む
149 隅でじっと待つ → ピコン！と跳ね上がって「呼バレタ？」→ 何も来ない → しぼんで「…呼バレテ／ナイ」
150 画面の数字が 1,2,3…7 と数える → 止まらず 8…28 と速くなり、体がふるえる → 「28！」で×目、くらっと傾く
151 扉の前（左）に止まって「到着／シマシタ」→ 前のめりに3回コツコツ → 返事がなく、画面が小さく「到着…」
152 右へ進んで止まり、くるっと左へ → 左へ進んで止まり、くるっと右へ（速くなる）→ 真ん中で「ココ／ドコ？」"""
import sys
import numpy as np
import motion30
import hakobu_motion as M
from hakobu_motion import T, ease, _approach

class Wait148:
    dur = 3.6; empty = True
    def motion(self, t):
        if t < 0.7: return _approach(t, 0.7, 220)
        if t < 2.2:                                      # きょろきょろ（左右に向きを変える）
            v = t - 0.7; side = 1 if int(v / 0.375) % 2 == 0 else -1
            return dict(flip=(side < 0), rot=side * 1.5)
        v = min(1, (t - 2.2) / 1.0)                      # ゆっくり沈む
        return dict(dy=14 * ease(v), sy=1 - 0.05 * ease(v), rot=3 * ease(v))
    def face(self, t):
        if t < 0.7: return ("open", "open"), None, "mid"
        if t < 2.2: return T("積ミ込ミ", "待チ") if t < 1.2 else (("open", "open"), None, "mid")
        return T("…" * (1 + min(2, int((t - 2.2) / 0.4))))
    def ring(self, t): return 0.6 if t < 2.2 else 0.3
    def blur(self, t): return t < 0.7
    def dim(self, t): return 1.0 if t < 2.2 else 1.0 - 0.15 * min(1, (t - 2.2) / 1.0)
    def cam(self, t): return (0, 0)

class Hope149:
    dur = 3.6; empty = True
    PING = 1.0
    def motion(self, t):
        if t < self.PING: return dict(dy=-2 * abs(np.sin(2 * np.pi * 0.7 * t)))
        v = t - self.PING
        if v < 0.6: return dict(dy=-50 * np.sin(np.pi * v / 0.6), sy=1 + 0.05 * np.sin(np.pi * v / 0.6))   # ピョン！
        if t < 2.4: return dict(rot=1.5 * np.sin(2 * np.pi * 1.5 * (t - 1.6)))                            # 待つ
        w = min(1, (t - 2.4) / 0.6)                                                                          # しぼむ
        return dict(sx=1 - 0.05 * ease(w), sy=1 - 0.1 * ease(w), dy=14 * ease(w))
    def face(self, t):
        if t < self.PING: return ("down", "down"), None, "closed"
        if t < 2.4: return T("呼バレタ？")
        return T("…呼バレテ", "ナイ")
    def ring(self, t): return 1.0 if self.PING <= t < 1.6 else 0.4
    def blur(self, t): return False
    def dim(self, t): return 1.0
    def cam(self, t): return (0, 0)

class Count150:
    dur = 3.6; empty = True
    def n(self, t):
        if t < 1.4: return 1 + int(t / 0.2)               # 1〜7 をゆっくり
        if t < 2.6: return min(28, 8 + int((t - 1.4) / 1.2 * 21))   # 8〜28 を速く
        return 28
    def motion(self, t):
        if t < 1.4: return dict(sy=1 - 0.02 * abs(np.sin(2 * np.pi * 2.5 * t)))
        if t < 2.6:
            r = (t - 1.4) / 1.2
            return dict(dx=r * 6 * np.sin(2 * np.pi * 14 * t), rot=r * 2 * np.sin(2 * np.pi * 11 * t))
        v = t - 2.6; k = min(1, v / 0.4)
        return dict(rot=-9 * ease(k), dx=-12 * ease(k))
    def face(self, t):
        if t < 2.6: return T(str(self.n(t)))
        if t < 2.9: return T("28！")
        return ("x", "x"), None, "wave"
    def ring(self, t): return 0.5 + 0.5 * np.sin(2 * np.pi * 6 * t) if 1.4 <= t < 2.6 else 0.6
    def blur(self, t): return False
    def dim(self, t): return 1.0
    def cam(self, t): return (int(5 * np.sin(2 * np.pi * 17 * t)), 0) if 2.0 <= t < 2.6 else (0, 0)

class Knock151:
    dur = 3.6; empty = True
    KN = (1.2, 1.55, 1.9)
    def motion(self, t):
        if t < 0.8: return _approach(t, 0.8, 240)
        for k in self.KN:                                 # 前のめりにコツッ
            if k <= t < k + 0.25:
                return dict(rot=-6 * np.sin(np.pi * (t - k) / 0.25), dx=-8 * np.sin(np.pi * (t - k) / 0.25))
        if t >= 2.4:
            w = min(1, (t - 2.4) / 0.5)
            return dict(sy=1 - 0.04 * ease(w), dy=8 * ease(w))
        return {}
    def face(self, t):
        if t < 0.8: return ("open", "open"), None, "mid"
        if t < 2.4: return T("到着", "シマシタ")
        return T("到着…")
    def ring(self, t): return 1.0 if any(k <= t < k + 0.25 for k in self.KN) else 0.5
    def blur(self, t): return t < 0.8
    def dim(self, t): return 1.0
    def cam(self, t): return (0, 0)

class Lost152:
    dur = 3.6; empty = True
    TURN = (0.0, 0.9, 1.6, 2.1, 2.45)                    # 向きを変える時刻（だんだん速く）
    STOP = 2.75
    def motion(self, t):
        if t >= self.STOP:
            v = t - self.STOP; k = np.exp(-v * 8) * np.cos(2 * np.pi * 4 * v)
            return dict(rot=3 * k)
        i = sum(1 for f in self.TURN if t >= f) - 1
        a = self.TURN[i]; b = self.TURN[i + 1] if i + 1 < len(self.TURN) else self.STOP
        u = ease((t - a) / (b - a)); side = -1 if i % 2 == 0 else 1      # 左へ・右へ
        x0 = 0 if i == 0 else (-120 if i % 2 == 1 else 120)
        x1 = -120 if side < 0 else 120
        if i == len(self.TURN) - 1: x1 = 0
        return dict(dx=x0 + (x1 - x0) * u, flip=(side > 0), dy=-3 * abs(np.sin(2 * np.pi * 4 * t)))
    def face(self, t):
        if t < self.STOP: return ("open", "open"), None, "o"
        return T("ココ", "ドコ？")
    def ring(self, t): return 0.6
    def blur(self, t): return t < self.STOP
    def dim(self, t): return 1.0
    def cam(self, t): return (0, 0)

M.PATTERNS.update(ep148=Wait148, ep149=Hope149, ep150=Count150, ep151=Knock151, ep152=Lost152)
if __name__ == "__main__":
    for arg in sys.argv[1:]:
        nm, _, kv = arg.partition(":")
        pat = M.PATTERNS[nm]()
        for item in filter(None, kv.split(",")):
            k, v = item.split("=", 1)
            if k in motion30.STR: setattr(pat, k, v)
            else: setattr(pat, k, float(v))
        M.render(pat, f"ハコブくん_{nm}.mp4")
