"""Billet 60: the four Mesh2Motion dances (CC0) on every humanoid model.

Headless Blender, one target per run:
  blender -b --factory-startup --python tools/blender/humanoid_dances.py -- \
      <human-base-animations.glb> <human-addon-animations.glb> <target.glb> <out.glb>

Same transfer as the hero's weapon gestures (hero_gestures.py): world-space
rotations bone by bone, limbs aligned on the source rest, no translation. A
Meshy rig (`Hips`, `LeftArm`…) or a Mixamo rig (`mixamorig:…`, fingers
included) is recognised by its bone names; tails and extra bones rest.
"""
import os
import sys

import bpy

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import hero_gestures as transfer  # noqa: E402

DANCES = [
    ('Dance_Simple', 'base', 'Dance_Simple'),
    ('Dance_Body_Roll', 'addon', 'Dance Body Roll'),
    ('Dance_Charleston', 'addon', 'Dance Charleston'),
    ('Dance_Reach_Hip', 'addon', 'Dance Reach Hip'),
]
MIXAMO = {
    'pelvis': 'Hips', 'spine_01': 'Spine', 'spine_02': 'Spine1', 'spine_03': 'Spine2',
    'neck_01': 'Neck', 'head': 'Head',
    'thigh_l': 'LeftUpLeg', 'calf_l': 'LeftLeg', 'foot_l': 'LeftFoot', 'ball_l': 'LeftToeBase',
    'thigh_r': 'RightUpLeg', 'calf_r': 'RightLeg', 'foot_r': 'RightFoot', 'ball_r': 'RightToeBase',
}
for side, word in (('l', 'Left'), ('r', 'Right')):
    MIXAMO.update({f'clavicle_{side}': f'{word}Shoulder', f'upperarm_{side}': f'{word}Arm',
                   f'lowerarm_{side}': f'{word}ForeArm', f'hand_{side}': f'{word}Hand'})
    for finger, name in (('thumb', 'Thumb'), ('index', 'Index'), ('middle', 'Middle'), ('ring', 'Ring'), ('pinky', 'Pinky')):
        for joint in (1, 2, 3):
            MIXAMO[f'{finger}_0{joint}_{side}'] = f'{word}Hand{name}{joint}'


def bone_map(armature):
    names = set(armature.data.bones.keys())
    prefix = next((n.split(':')[0] + ':' for n in names if n.endswith(':Hips')), None)
    if prefix:
        return {s: prefix + h for s, h in MIXAMO.items()}
    if {'Hips', 'LeftArm'} <= names:
        return transfer.BONES
    raise SystemExit('Unknown humanoid rig: ' + ', '.join(sorted(names)[:8]))


def main():
    base_path, addon_path, target_path, out_path = sys.argv[sys.argv.index('--') + 1:][:4]
    bpy.ops.wm.read_factory_settings(use_empty=True)
    target, target_objects, target_actions = transfer.load(target_path)
    original = target.animation_data.action if target.animation_data else None
    bones = bone_map(target)
    sources = {'base': transfer.load(base_path), 'addon': transfer.load(addon_path)}
    made = []
    for name, key, action_name in DANCES:
        if name in target_actions:
            continue
        armature, _, actions = sources[key]
        made.append((transfer.retarget(target, armature, actions[action_name], name + '__dance', bones), name))
    for armature, objects, actions in sources.values():
        for action in actions.values():
            bpy.data.actions.remove(action)
        for obj in objects:
            bpy.data.objects.remove(obj, do_unlink=True)
    for clip, name in made:
        clip.name = name
    if original:
        transfer.assign(target, original)
    bpy.ops.object.select_all(action='DESELECT')
    for obj in target_objects:
        obj.select_set(True)
    bpy.context.view_layer.objects.active = target
    bpy.ops.export_scene.gltf(filepath=out_path, export_format='GLB', use_selection=True,
                              use_active_scene=True, export_animations=True, export_animation_mode='ACTIONS')
    print('HUMANOID DANCES', os.path.basename(target_path), len(target_actions), '+', len(made), '->', out_path)


if __name__ == '__main__':
    main()
