import { Logger } from "@babylonjs/core/Misc/logger";
import { MmdAnimation } from "./Animation/mmdAnimation";
import { MmdBoneAnimationTrack, MmdCameraAnimationTrack, MmdMorphAnimationTrack, MmdMovableBoneAnimationTrack, MmdPropertyAnimationTrack } from "./Animation/mmdAnimationTrack";
import { VpdReader } from "./Parser/vpdReader";
/**
 * VpdLoader is a loader that loads MMD pose data in VPD format
 *
 * VPD format is a binary format for MMD animation data
 */
export class VpdLoader {
    _scene;
    _textDecoder;
    _loggingEnabled;
    /** @internal */
    log;
    /** @internal */
    warn;
    /** @internal */
    error;
    /**
     * Create a new VpdLoader
     * @param scene Scene for loading file
     */
    constructor(scene) {
        this._loggingEnabled = false;
        this.log = this._logDisabled;
        this.warn = this._warnDisabled;
        this.error = this._errorDisabled;
        this._scene = scene;
        this._textDecoder = new TextDecoder("shift_jis");
    }
    /**
     * Load MMD animation data from VPD object
     * @param name Animation name
     * @param vpdObject VPD object
     * @returns MMD animation data
     */
    loadFromVpdObject(name, vpdObject) {
        const bones = vpdObject.bones;
        const boneNames = Object.keys(bones);
        let movableBoneCount = 0;
        for (let i = 0; i < boneNames.length; i++) {
            if (bones[boneNames[i]].position !== undefined) {
                movableBoneCount += 1;
            }
        }
        const boneTracks = new Array(boneNames.length - movableBoneCount);
        const movableBoneTracks = new Array(movableBoneCount);
        let boneTrackIndex = 0;
        let movableBoneTrackIndex = 0;
        for (let i = 0; i < boneNames.length; i++) {
            const boneName = boneNames[i];
            const bone = bones[boneName];
            if (bone.position === undefined) {
                const boneTrack = boneTracks[boneTrackIndex] = new MmdBoneAnimationTrack(boneName, 1);
                boneTrack.rotations[0] = bone.rotation[0];
                boneTrack.rotations[1] = bone.rotation[1];
                boneTrack.rotations[2] = bone.rotation[2];
                boneTrack.rotations[3] = bone.rotation[3];
                boneTrackIndex += 1;
            }
            else {
                const movableBoneTrack = movableBoneTracks[movableBoneTrackIndex] = new MmdMovableBoneAnimationTrack(boneName, 1);
                movableBoneTrack.positions[0] = bone.position[0];
                movableBoneTrack.positions[1] = bone.position[1];
                movableBoneTrack.positions[2] = bone.position[2];
                movableBoneTrack.rotations[0] = bone.rotation[0];
                movableBoneTrack.rotations[1] = bone.rotation[1];
                movableBoneTrack.rotations[2] = bone.rotation[2];
                movableBoneTrack.rotations[3] = bone.rotation[3];
                movableBoneTrackIndex += 1;
            }
        }
        const morphs = vpdObject.morphs;
        const morphNames = Object.keys(morphs);
        const morphTracks = new Array(morphNames.length);
        for (let i = 0; i < morphNames.length; i++) {
            const morphName = morphNames[i];
            const morphTrack = morphTracks[i] = new MmdMorphAnimationTrack(morphName, 1);
            morphTrack.weights[0] = morphs[morphName];
        }
        return new MmdAnimation(name, boneTracks, movableBoneTracks, morphTracks, new MmdPropertyAnimationTrack(0, []), new MmdCameraAnimationTrack(0));
    }
    /**
     * Load MMD animation data from VPD array buffer
     * @param name Animation name
     * @param buffer VPD array buffer
     * @returns Animation data
     * @throws {LoadFileError} when validation fails
     */
    loadFromBuffer(name, buffer) {
        const text = this._textDecoder.decode(buffer);
        const vpdObject = VpdReader.Parse(text, this);
        return this.loadFromVpdObject(name, vpdObject);
    }
    /**
     * Load MMD animation data from VPD file or URL
     * @param name Animation name
     * @param fileOrUrl VPD file or URL
     * @param onLoad Callback function that is called when load is complete
     * @param onProgress Callback function that is called while loading
     * @param onError Callback function that is called when loading is failed
     * @returns File request
     */
    load(name, fileOrUrl, onLoad, onProgress, onError) {
        return this._scene._loadFile(fileOrUrl, (data) => onLoad(this.loadFromBuffer(name, data)), onProgress, true, true, onError);
    }
    /**
     * Load MMD animation data from VPD file or URL asynchronously
     * @param name Animation name
     * @param fileOrUrl VPD file or URL
     * @param onProgress Callback function that is called while loading
     */
    loadAsync(name, fileOrUrl, onProgress) {
        return new Promise((resolve, reject) => {
            this.load(name, fileOrUrl, resolve, onProgress, (request, exception) => reject({ request, exception }));
        });
    }
    /**
     * Enable or disable debug logging (default: false)
     */
    get loggingEnabled() {
        return this._loggingEnabled;
    }
    set loggingEnabled(value) {
        this._loggingEnabled = value;
        if (value) {
            this.log = this._logEnabled;
            this.warn = this._warnEnabled;
            this.error = this._errorEnabled;
        }
        else {
            this.log = this._logDisabled;
            this.warn = this._warnDisabled;
            this.error = this._errorDisabled;
        }
    }
    _logEnabled(message) {
        Logger.Log(message);
    }
    _logDisabled() {
        // do nothing
    }
    _warnEnabled(message) {
        Logger.Warn(message);
    }
    _warnDisabled() {
        // do nothing
    }
    _errorEnabled(message) {
        Logger.Error(message);
    }
    _errorDisabled() {
        // do nothing
    }
}
