"use strict";
/**
 * AudioDecoder - Decodes encoded audio data to AudioData
 * Implements the W3C WebCodecs AudioDecoder interface
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.AudioDecoder = void 0;
const AudioData_1 = require("./AudioData");
const codec_registry_1 = require("./codec-registry");
const types_1 = require("./types");
// Load native addon
let native;
try {
    native = require('../build/Release/webcodecs_node.node');
}
catch {
    native = null;
}
class AudioDecoder {
    static async isConfigSupported(config) {
        const supported = (0, codec_registry_1.isAudioCodecSupported)(config.codec);
        return { supported, config };
    }
    constructor(init) {
        this._state = 'unconfigured';
        this._decodeQueueSize = 0;
        this._config = null;
        if (!init.output || typeof init.output !== 'function') {
            throw new TypeError('output callback is required');
        }
        if (!init.error || typeof init.error !== 'function') {
            throw new TypeError('error callback is required');
        }
        this._outputCallback = init.output;
        this._errorCallback = init.error;
        if (native) {
            this._native = new native.AudioDecoderNative(this._onData.bind(this), this._onError.bind(this));
        }
    }
    get state() {
        return this._state;
    }
    get decodeQueueSize() {
        return this._decodeQueueSize;
    }
    configure(config) {
        if (this._state === 'closed') {
            throw new types_1.DOMException('Decoder is closed', 'InvalidStateError');
        }
        if (!(0, codec_registry_1.isAudioCodecSupported)(config.codec)) {
            throw new types_1.DOMException(`Unsupported codec: ${config.codec}`, 'NotSupportedError');
        }
        if (!native) {
            throw new types_1.DOMException('Native addon not available', 'NotSupportedError');
        }
        const ffmpegCodec = (0, codec_registry_1.getFFmpegAudioDecoder)(config.codec);
        const codecParams = {
            codec: ffmpegCodec,
            sampleRate: config.sampleRate,
            channels: config.numberOfChannels,
        };
        if (config.description) {
            let desc;
            if (config.description instanceof ArrayBuffer) {
                desc = new Uint8Array(config.description);
            }
            else {
                desc = new Uint8Array(config.description.buffer, config.description.byteOffset, config.description.byteLength);
            }
            codecParams.extradata = Buffer.from(desc);
        }
        this._native.configure(codecParams);
        this._config = config;
        this._state = 'configured';
    }
    decode(chunk) {
        if (this._state !== 'configured') {
            throw new types_1.DOMException('Decoder is not configured', 'InvalidStateError');
        }
        const data = new Uint8Array(chunk.byteLength);
        chunk.copyTo(data);
        this._decodeQueueSize++;
        this._native.decode(Buffer.from(data), chunk.type === 'key', chunk.timestamp, chunk.duration ?? 0);
    }
    async flush() {
        if (this._state !== 'configured') {
            throw new types_1.DOMException('Decoder is not configured', 'InvalidStateError');
        }
        return new Promise((resolve, reject) => {
            this._native.flush((err) => {
                if (err) {
                    reject(new types_1.DOMException(err.message, 'EncodingError'));
                }
                else {
                    resolve();
                }
            });
        });
    }
    reset() {
        if (this._state === 'closed') {
            throw new types_1.DOMException('Decoder is closed', 'InvalidStateError');
        }
        if (this._native) {
            this._native.reset();
        }
        this._decodeQueueSize = 0;
        this._state = 'unconfigured';
        this._config = null;
    }
    close() {
        if (this._state === 'closed')
            return;
        if (this._native) {
            this._native.close();
        }
        this._state = 'closed';
        this._decodeQueueSize = 0;
        this._config = null;
    }
    _onData(buffer, format, sampleRate, numberOfFrames, numberOfChannels, timestamp) {
        this._decodeQueueSize = Math.max(0, this._decodeQueueSize - 1);
        try {
            const audioData = new AudioData_1.AudioData({
                format: format,
                sampleRate,
                numberOfFrames,
                numberOfChannels,
                timestamp,
                data: new Uint8Array(buffer).buffer,
            });
            this._outputCallback(audioData);
        }
        catch (e) {
            console.error('AudioDecoder output callback error:', e);
        }
    }
    _onError(message) {
        try {
            this._errorCallback(new types_1.DOMException(message, 'EncodingError'));
        }
        catch (e) {
            // Don't propagate callback errors
        }
    }
}
exports.AudioDecoder = AudioDecoder;
//# sourceMappingURL=AudioDecoder.js.map