/**
 * R15Skeleton — резолвер костей R15-скелета для скинов веб-плеера.
 *
 * R15-скины (bacon-hair и др.) приходят как glTF с УЖЕ встроенным
 * скелетом Mixamo. Имена костей — `mixamorig:Hips`, `mixamorig:RightArm`,
 * иногда с суффиксами (`_01`) или с заменой `:` → `_`. Этот модуль даёт
 * стабильный маппинг "логическое R15-имя" → реальная Babylon.Bone.
 *
 * Логические R15-имена (16 частей, стандарт Roblox):
 *   HumanoidRootPart, LowerTorso, UpperTorso, Head,
 *   LeftUpperArm, LeftLowerArm, LeftHand, RightUpperArm, RightLowerArm, RightHand,
 *   LeftUpperLeg, LeftLowerLeg, LeftFoot, RightUpperLeg, RightLowerLeg, RightFoot
 *
 * Порт логики `_resolve_bone` из Godot (RubloxAnimator.gd) и
 * R15Skeleton-резолва из Kotlin (R15Animator.kt).
 */

// R15-логическое имя → список кандидатов Mixamo-имён (по приоритету).
// Резолвер ищет кость, чьё имя СОДЕРЖИТ один из кандидатов (без учёта
// регистра, после нормализации `:` и `_`).
const R15_TO_MIXAMO = {
    HumanoidRootPart: ['hips', 'humanoidrootpart', 'root'],
    LowerTorso:       ['spine', 'lowertorso', 'hips'],
    UpperTorso:       ['spine2', 'spine1', 'uppertorso', 'spine'],
    Head:             ['head'],
    Neck:             ['neck'],
    LeftUpperArm:     ['leftarm', 'leftupperarm', 'leftshoulder'],
    LeftLowerArm:     ['leftforearm', 'leftlowerarm'],
    LeftHand:         ['lefthand'],
    RightUpperArm:    ['rightarm', 'rightupperarm', 'rightshoulder'],
    RightLowerArm:    ['rightforearm', 'rightlowerarm'],
    RightHand:        ['righthand'],
    LeftUpperLeg:     ['leftupleg', 'leftupperleg'],
    LeftLowerLeg:     ['leftleg', 'leftlowerleg'],
    LeftFoot:         ['leftfoot'],
    RightUpperLeg:    ['rightupleg', 'rightupperleg'],
    RightLowerLeg:    ['rightleg', 'rightlowerleg'],
    RightFoot:        ['rightfoot'],
};

/** Нормализовать имя кости: lower-case, убрать `mixamorig`, `:`, `_`, пробелы. */
function normBoneName(raw) {
    return String(raw || '')
        .toLowerCase()
        .replace(/mixamorig/g, '')
        .replace(/[:_\s.]/g, '');
}

export class R15Skeleton {
    /**
     * @param {Skeleton} skeleton — Babylon-скелет из загруженного скина.
     */
    constructor(skeleton) {
        this.skeleton = skeleton;
        // Кеш: R15-логическое имя → Bone (или null если не нашли).
        this._cache = {};
        // Нормализованное имя → Bone (один проход по скелету).
        this._normIndex = {};
        if (skeleton && skeleton.bones) {
            for (const bone of skeleton.bones) {
                this._normIndex[normBoneName(bone.name)] = bone;
            }
        }
    }

    /**
     * Найти кость по логическому R15-имени. Возвращает Babylon.Bone или null.
     * Результат кешируется.
     */
    resolveBone(logicalName) {
        if (logicalName in this._cache) return this._cache[logicalName];
        const candidates = R15_TO_MIXAMO[logicalName];
        let found = null;
        if (candidates) {
            // 1. Точное совпадение нормализованного имени.
            for (const cand of candidates) {
                if (this._normIndex[cand]) { found = this._normIndex[cand]; break; }
            }
            // 2. Подстрока — на случай суффиксов (`rightarm01`, `head_end`).
            if (!found) {
                for (const cand of candidates) {
                    for (const normName in this._normIndex) {
                        if (normName.includes(cand)) { found = this._normIndex[normName]; break; }
                    }
                    if (found) break;
                }
            }
        }
        this._cache[logicalName] = found;
        return found;
    }

    /**
     * Проверка: является ли скелет валидным R15-скелетом.
     * Минимальный набор — должны резолвиться торс, голова, обе руки/ноги.
     */
    isValidR15() {
        const required = [
            'Head', 'UpperTorso',
            'LeftUpperArm', 'RightUpperArm',
            'LeftUpperLeg', 'RightUpperLeg',
        ];
        for (const name of required) {
            if (!this.resolveBone(name)) return false;
        }
        return true;
    }

    /** Список всех успешно зарезолвленных логических костей (для диагностики). */
    resolvedNames() {
        const out = [];
        for (const logical in R15_TO_MIXAMO) {
            if (this.resolveBone(logical)) out.push(logical);
        }
        return out;
    }
}
