import { assert } from "../../../../../core/assert.js";
import { string_compute_common_prefix } from "../../../../../core/primitives/strings/string_compute_common_prefix.js";
import { string_compute_similarity } from "../../../../../core/primitives/strings/string_compute_similarity.js";
import { HumanoidBoneType } from "./HumanoidBoneType.js";


/**
 *
 * @param {{distance:number}} a
 * @param {{distance:number}} b
 * @returns {number}
 */
function byDistance(a, b) {
    return b.distance - a.distance;
}

export class BoneMapping {
    constructor() {
        this.mapping = [];
    }

    /**
     *
     * @param {string[]} names
     */
    build(names) {
        //strip common prefix
        const commonPrefix = string_compute_common_prefix(names);
        const commonPrefixLength = commonPrefix.length;

        const conditionedNames = names.map(function (name) {
            return name.substring(commonPrefixLength);
        });

        const boneValues = Object.values(HumanoidBoneType);

        //compute probability distribution for each name to a known bone
        const matches = [];

        boneValues.forEach(function (boneName, boneIndex) {

            conditionedNames.forEach(function (name, inputIndex) {
                const distance = string_compute_similarity(name, boneName);
                const match = {
                    boneIndex,
                    inputIndex,
                    distance
                };
                matches.push(match);
            });

        });

        //sort distribution according to distance
        matches.sort(byDistance);

        //assign mapping
        let numMatches = matches.length;

        while (numMatches > 0) {
            const match = matches.pop();

            numMatches--;

            const boneIndex = match.boneIndex;
            const inputIndex = match.inputIndex;

            const boneValue = boneValues[boneIndex];

            this.mapping[inputIndex] = boneValue;

            //remove all references to this bone index and input
            for (let i = 0; i < numMatches; i++) {
                const match = matches[i];
                if (match.boneIndex === boneIndex || match.inputIndex === inputIndex) {
                    matches.splice(i, 1);

                    numMatches--;
                    i--;
                }
            }
        }
    }

    /**
     * @param {Bone[]} bones
     */
    apply(bones) {
        assert.ok(Array.isArray(bones), 'bones argument must be an array');

        const numBones = bones.length;

        for (let i = 0; i < numBones; i++) {
            const bone = bones[i];

            const boneType = this.mapping[i];

            bone.boneType = boneType;
        }
    }
}
