#!/usr/bin/env python3
"""Add a gentle looping insertion animation to the board GLB:
- PIN_* nodes translate up by LIFT mm then back (pins press into their own holes)
- SHADOW_* nodes scale up as the pin lifts (the contact shadow spreads/softens), so the
  shadow VISIBLY animates in the native viewer regardless of any ground-plane behavior."""
import sys, numpy as np, pygltflib
from pygltflib import GLTF2, Animation, AnimationSampler, AnimationChannel, AnimationChannelTarget, Accessor, BufferView
IN, OUT = sys.argv[1], sys.argv[2]
LIFT = float(sys.argv[3])/1000.0   # mm -> meters

g = GLTF2().load(IN)
pin_nodes    = [i for i,n in enumerate(g.nodes) if n.name and n.name.startswith("PIN_")]
shadow_nodes = [i for i,n in enumerate(g.nodes) if n.name and n.name.startswith("SHADOW_")]
print("pins:", [g.nodes[i].name for i in pin_nodes], "shadows:", [g.nodes[i].name for i in shadow_nodes])

times = np.array([0.0,1.4,3.0,4.4,6.0], dtype=np.float32)
trans = np.array([[0,0,0],[0,0,0],[0,0,LIFT],[0,0,LIFT],[0,0,0]], dtype=np.float32)
scale = np.array([[1,1,1],[1,1,1],[1.7,1.7,1],[1.7,1.7,1],[1,1,1]], dtype=np.float32)  # shadow spread

blob = bytearray(g.binary_blob())
def add_view(b):
    while len(blob)%4: blob.append(0)
    off=len(blob); blob.extend(b)
    g.bufferViews.append(BufferView(buffer=0, byteOffset=off, byteLength=len(b)))
    return len(g.bufferViews)-1
def add_acc(view, count, typ, mn=None, mx=None):
    g.accessors.append(Accessor(bufferView=view, componentType=pygltflib.FLOAT, count=count, type=typ, min=mn, max=mx))
    return len(g.accessors)-1

t_idx  = add_acc(add_view(times.tobytes()), len(times), "SCALAR", [float(times.min())],[float(times.max())])
tr_idx = add_acc(add_view(trans.tobytes()), len(trans), "VEC3")
sc_idx = add_acc(add_view(scale.tobytes()), len(scale), "VEC3")

samplers=[]; channels=[]
for pn in pin_nodes:
    samplers.append(AnimationSampler(input=t_idx, output=tr_idx, interpolation="LINEAR"))
    channels.append(AnimationChannel(sampler=len(samplers)-1, target=AnimationChannelTarget(node=pn, path="translation")))
for sn in shadow_nodes:
    samplers.append(AnimationSampler(input=t_idx, output=sc_idx, interpolation="LINEAR"))
    channels.append(AnimationChannel(sampler=len(samplers)-1, target=AnimationChannelTarget(node=sn, path="scale")))
g.animations=[Animation(name="insert", samplers=samplers, channels=channels)]
g.buffers[0].byteLength=len(blob); g.set_binary_blob(bytes(blob))
g.save(OUT)
print(f"animation: {len(pin_nodes)} pins lift {LIFT*1000}mm + {len(shadow_nodes)} shadows spread -> {OUT}")