import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector";
import { PmxObject } from "../../Loader/Parser/pmxObject";
import { WasmBufferedArraySpan } from "./wasmBufferedArray";
/**
 * Bone for MMD WASM runtime
 *
 * For mmd wasm runtime, it is necessary to override the bone system because it has a different implementation than the usual matrix update method
 *
 * Which requires the mmd wasm runtime bone, which is the wrapper of the babylon.js bone
 */
export class MmdWasmRuntimeBone {
    /**
     * The Babylon.js bone
     */
    linkedBone;
    /**
     * Name of the bone
     */
    name;
    /**
     * Parent bone
     */
    parentBone;
    /**
     * Child bones
     */
    childBones;
    /**
     * Transform order
     */
    transformOrder;
    /**
     * Bone flag
     *
     * @see PmxObject.Bone.Flag
     */
    flag;
    /**
     * Whether the bone transform is applied after physics
     */
    transformAfterPhysics;
    _worldMatrix;
    /**
     * World matrix of this bone
     *
     * Slice of `MmdModel.worldTransformMatrices` that corresponds to this bone
     *
     * This array reference should not be copied elsewhere and must be read and written with minimal scope
     */
    get worldMatrix() {
        return this._worldMatrix.array;
    }
    /**
     * Get ik solver index
     *
     * If the bone does not have an ik solver, it will return -1
     */
    ikSolverIndex;
    _boneIndex;
    _mmdRuntime;
    _mmdModelPtr;
    /**
     * Create MMD WASM runtime bone
     * @param linkedBone Linked Babylon.js bone
     * @param boneMetadata Bone metadata
     * @param worldTransformMatrices WASM buffered array of world transform matrices
     * @param boneIndex Bone index
     * @param ikSolverIndex IK solver index
     * @param mmdRuntime MMD WASM runtime
     * @param mmdModelPtr MMD WASM side model pointer
     */
    constructor(linkedBone, boneMetadata, worldTransformMatrices, boneIndex, ikSolverIndex, mmdRuntime, mmdModelPtr) {
        this.linkedBone = linkedBone;
        this.name = boneMetadata.name;
        this.parentBone = null;
        this.childBones = [];
        this.transformOrder = boneMetadata.transformOrder;
        this.flag = boneMetadata.flag;
        this.transformAfterPhysics = (boneMetadata.flag & PmxObject.Bone.Flag.TransformAfterPhysics) !== 0;
        this._worldMatrix = new WasmBufferedArraySpan(mmdRuntime.wasmInstance, worldTransformMatrices, boneIndex * 16 * 4, 16);
        this.ikSolverIndex = ikSolverIndex;
        this._boneIndex = boneIndex;
        this._mmdRuntime = mmdRuntime;
        this._mmdModelPtr = mmdModelPtr;
    }
    /**
     * @internal
     * @param wasmInstance MMD WASM instance
     */
    updateBackBufferReference(wasmInstance) {
        this._worldMatrix.updateBackBufferReference(wasmInstance);
    }
    /**
     * Update the world matrix of this bone and its children bones recursively
     */
    updateWorldMatrix() {
        if (this._mmdRuntime.usingWasmBackBuffer) {
            this._mmdRuntime.wasmInternal.updateBackBufferBoneWorldMatrix(this._mmdModelPtr, this._boneIndex);
        }
        else {
            this._mmdRuntime.wasmInternal.updateBoneWorldMatrix(this._mmdModelPtr, this._boneIndex);
        }
    }
    /**
     * Get the world matrix of this bone
     *
     * The result of this method is not same as `linkedBone.getFinalMatrix()`
     *
     * `linkedBone.getFinalMatrix()` updated at the end of the mmd runtime update, so it may not be the latest value
     * @param target target matrix
     * @returns target matrix
     */
    getWorldMatrixToRef(target) {
        return Matrix.FromArrayToRef(this._worldMatrix.array, 0, target);
    }
    /**
     * Get the world translation of this bone
     * @param target target vector
     * @returns target vector
     */
    getWorldTranslationToRef(target) {
        return Vector3.FromArrayToRef(this._worldMatrix.array, 12, target);
    }
    /**
     * Set the world translation of this bone
     * @param source source vector
     */
    setWorldTranslationFromRef(source) {
        const worldMatrix = this._worldMatrix.array;
        worldMatrix[12] = source.x;
        worldMatrix[13] = source.y;
        worldMatrix[14] = source.z;
    }
}
