/**
 * @license
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file ResonanceAudio library name space and common utilities.
 * @author Andrew Allen <bitllama@google.com>
 */
'use strict';
// Internal dependencies.
import Listener from './listener.js';
import Source from './source.js';
import Room from './room.js';
import Encoder from './encoder.js';
import Utils from './utils.js';
/**
 * Options for constructing a new ResonanceAudio scene.
 * @typedef {Object} ResonanceAudio~ResonanceAudioOptions
 * @property {Number} ambisonicOrder
 * Desired ambisonic Order. Defaults to
 * {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
 * @property {Float32Array} listenerPosition
 * The listener's initial position (in meters), where origin is the center of
 * the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
 * @property {Float32Array} listenerForward
 * The listener's initial forward vector.
 * Defaults to {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
 * @property {Float32Array} listenerUp
 * The listener's initial up vector.
 * Defaults to {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
 * @property {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
 * {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
 * @property {Utils~RoomMaterials} materials Named acoustic materials per wall.
 * Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
 * @property {Number} speedOfSound
 * (in meters/second). Defaults to
 * {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
 */
/**
 * @class ResonanceAudio
 * @description Main class for managing sources, room and listener models.
 * @param {AudioContext} context
 * Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
 * @param {ResonanceAudio~ResonanceAudioOptions} options
 * Options for constructing a new ResonanceAudio scene.
 */
class ResonanceAudio {
    constructor(context, options) {
        // Public variables.
        /**
         * Binaurally-rendered stereo (2-channel) output {@link
         * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
         * @member {AudioNode} output
         * @memberof ResonanceAudio
         * @instance
         */
        /**
         * Ambisonic (multichannel) input {@link
         * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}
         * (For rendering input soundfields).
         * @member {AudioNode} ambisonicInput
         * @memberof ResonanceAudio
         * @instance
         */
        /**
         * Ambisonic (multichannel) output {@link
         * https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}
         * (For allowing external rendering / post-processing).
         * @member {AudioNode} ambisonicOutput
         * @memberof ResonanceAudio
         * @instance
         */
        // Use defaults for undefined arguments.
        if (options == undefined) {
            options = {};
        }
        if (options.ambisonicOrder == undefined) {
            options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER;
        }
        if (options.listenerPosition == undefined) {
            options.listenerPosition = Utils.DEFAULT_POSITION.slice();
        }
        if (options.listenerForward == undefined) {
            options.listenerForward = Utils.DEFAULT_FORWARD.slice();
        }
        if (options.listenerUp == undefined) {
            options.listenerUp = Utils.DEFAULT_UP.slice();
        }
        if (options.dimensions == undefined) {
            options.dimensions = {};
            Object.assign(options.dimensions, Utils.DEFAULT_ROOM_DIMENSIONS);
        }
        if (options.materials == undefined) {
            options.materials = {};
            Object.assign(options.materials, Utils.DEFAULT_ROOM_MATERIALS);
        }
        if (options.speedOfSound == undefined) {
            options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND;
        }
        // Create member submodules.
        this._ambisonicOrder = Encoder.validateAmbisonicOrder(options.ambisonicOrder);
        this._sources = [];
        this._room = new Room(context, {
            listenerPosition: options.listenerPosition,
            dimensions: options.dimensions,
            materials: options.materials,
            speedOfSound: options.speedOfSound,
        });
        this._listener = new Listener(context, {
            ambisonicOrder: options.ambisonicOrder,
            position: options.listenerPosition,
            forward: options.listenerForward,
            up: options.listenerUp,
        });
        // Create auxillary audio nodes.
        this._context = context;
        this.output = context.createGain();
        this.ambisonicOutput = context.createGain();
        this.ambisonicInput = this._listener.input;
        // Connect audio graph.
        this._room.output.connect(this._listener.input);
        this._listener.output.connect(this.output);
        this._listener.ambisonicOutput.connect(this.ambisonicOutput);
    }
    /**
     * Create a new source for the scene.
     * @param {Source~SourceOptions} options
     * Options for constructing a new Source.
     * @return {Source}
     */
    createSource(options) {
        // Create a source and push it to the internal sources array, returning
        // the object's reference to the user.
        let source = new Source(this, options);
        this._sources[this._sources.length] = source;
        return source;
    }
    /**
     * Set the scene's desired ambisonic order.
     * @param {Number} ambisonicOrder Desired ambisonic order.
     */
    setAmbisonicOrder(ambisonicOrder) {
        this._ambisonicOrder = Encoder.validateAmbisonicOrder(ambisonicOrder);
    }
    /**
     * Set the room's dimensions and wall materials.
     * @param {Object} dimensions Room dimensions (in meters).
     * @param {Object} materials Named acoustic materials per wall.
     */
    setRoomProperties(dimensions, materials) {
        this._room.setProperties(dimensions, materials);
    }
    /**
     * Set the listener's position (in meters), where origin is the center of
     * the room.
     * @param {Number} x
     * @param {Number} y
     * @param {Number} z
     */
    setListenerPosition(x, y, z) {
        // Update listener position.
        this._listener.position[0] = x;
        this._listener.position[1] = y;
        this._listener.position[2] = z;
        this._room.setListenerPosition(x, y, z);
        // Update sources with new listener position.
        this._sources.forEach(element => {
            element._update();
        });
    }
    /**
     * Set the source's orientation using forward and up vectors.
     * @param {Number} forwardX
     * @param {Number} forwardY
     * @param {Number} forwardZ
     * @param {Number} upX
     * @param {Number} upY
     * @param {Number} upZ
     */
    setListenerOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) {
        this._listener.setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ);
    }
    /**
     * Set the listener's position and orientation using a Three.js Matrix4 object.
     * @param {Object} matrix
     * The Three.js Matrix4 object representing the listener's world transform.
     */
    setListenerFromMatrix(matrix) {
        this._listener.setFromMatrix(matrix);
        // Update the rest of the scene using new listener position.
        this.setListenerPosition(this._listener.position[0], this._listener.position[1], this._listener.position[2]);
    }
    /**
     * Set the speed of sound.
     * @param {Number} speedOfSound
     */
    setSpeedOfSound(speedOfSound) {
        this._room.speedOfSound = speedOfSound;
    }
}
export default ResonanceAudio;
