import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector";
import { PmxObject } from "../Loader/Parser/pmxObject";
/**
 * Gives the effect of being in a child parent relationship regardless of the bone hierarchy. Similar to Unity's Parent constraint
 */
export class AppendTransformSolver {
    isLocal;
    /**
     * Whether to affect bone rotation
     */
    affectRotation;
    /**
     * Whether to affect bone position
     */
    affectPosition;
    /**
     * The ratio of the effect (can be negative)
     */
    ratio;
    /**
     * Affecting bones
     *
     * Similar to parent in bone hierarchy
     */
    targetBone;
    /**
     * The offset of the position to be affected
     */
    appendPositionOffset = Vector3.Zero();
    /**
     * The offset of the rotation to be affected
     */
    appendRotationOffset = Quaternion.Identity();
    /**
     * Creates append transform solver
     * @param boneFlag bone flag of the bone to be affected
     * @param boneAppendTransformMetadata metadata of the bone to be affected
     * @param targetBone affecting bone
     */
    constructor(boneFlag, boneAppendTransformMetadata, targetBone) {
        this.isLocal = (boneFlag & PmxObject.Bone.Flag.LocalAppendTransform) !== 0;
        this.affectRotation = (boneFlag & PmxObject.Bone.Flag.HasAppendRotate) !== 0;
        this.affectPosition = (boneFlag & PmxObject.Bone.Flag.HasAppendMove) !== 0;
        this.ratio = boneAppendTransformMetadata.ratio;
        this.targetBone = targetBone;
    }
    static _IdentityQuaternion = Quaternion.Identity();
    /**
     * Updates the solver
     */
    update() {
        const targetBone = this.targetBone;
        if (this.affectRotation) {
            const appendRotationOffset = this.appendRotationOffset;
            if (this.isLocal) {
                // Since mmd bones all have identity quaternions, we abandon the compatibility for skeletons that don't and improve performance
                // targetBone.getAnimationRotationOffsetToRef(appendRotationOffset);
                targetBone.getAnimatedRotationToRef(appendRotationOffset);
            }
            else {
                if (targetBone.appendTransformSolver !== null) {
                    appendRotationOffset.copyFrom(targetBone.appendTransformSolver.appendRotationOffset);
                }
                else {
                    // targetBone.getAnimationRotationOffsetToRef(appendRotationOffset);
                    targetBone.getAnimatedRotationToRef(appendRotationOffset);
                }
            }
            if (targetBone.ikRotation !== null) {
                targetBone.ikRotation.multiplyToRef(appendRotationOffset, appendRotationOffset);
            }
            Quaternion.SlerpToRef(AppendTransformSolver._IdentityQuaternion, appendRotationOffset, this.ratio, appendRotationOffset);
        }
        if (this.affectPosition) {
            const appendPositionOffset = this.appendPositionOffset;
            if (this.isLocal) {
                targetBone.getAnimationPositionOffsetToRef(appendPositionOffset);
            }
            else {
                if (targetBone.appendTransformSolver !== null) {
                    appendPositionOffset.copyFrom(targetBone.appendTransformSolver.appendPositionOffset);
                }
                else {
                    targetBone.getAnimationPositionOffsetToRef(appendPositionOffset);
                }
            }
            appendPositionOffset.scaleInPlace(this.ratio);
        }
    }
}
