/* global using */

var tools = require("../tools/tools");
var Howl = require("howler").Howl;

var warn = tools.warn;
var truthy = tools.truthy;

/**
 * Constructor for the <audio> asset.
 * 
 * @param asset [XML DOM Element] The asset definition.
 * @param interpreter [object] The interpreter instance.
 * @trigger wse.interpreter.warning@interpreter
 * @trigger wse.assets.audio.constructor@interpreter
 */
function Audio (asset, interpreter) {

    var sources, i, len, j, jlen, current, track, trackName;
    var trackFiles, href, type, source, tracks, bus, trackSettings;

    bus = interpreter.bus;

    this.interpreter = interpreter;
    this.bus = bus;
    this.name = asset.getAttribute("name");
    this.tracks = {};
    this.autopause = truthy(asset.getAttribute("autopause"));
    this.loop = truthy(asset.getAttribute("loop"));
    this.fade = truthy(asset.getAttribute("fade"));
    this.fadeinDuration = parseInt(asset.getAttribute("fadein")) || 1000;
    this.fadeoutDuration = parseInt(asset.getAttribute("fadeout")) || 1000;
    this._playing = false;

    tracks = asset.getElementsByTagName("track");
    len = tracks.length;

    if (len < 1) {

        warn(this.bus, "No tracks defined for audio element '" + this.name + "'.", asset);

        return {
            doNext: true
        };
    }

    // check all sources and create Howl instances:
    for (i = 0; i < len; i += 1) {

        current = tracks[i];

        trackName = current.getAttribute("title");

        if (trackName === null) {

            warn(this.bus, "No title defined for track '" + trackName + 
                "' in audio element '" + this.name + "'.", asset);

            continue;
        }

        sources = current.getElementsByTagName("source");
        jlen = sources.length;

        if (jlen < 1) {

            warn(this.bus, "No sources defined for track '" + trackName +
                "' in audio element '" + this.name + "'.", asset);

            continue;
        }

        trackSettings = {
            urls: [],
            autoplay: false,
            loop: this.loop || false,
            onload: this.bus.trigger.bind(this.bus, "wse.assets.loading.decrease")
        };

        trackFiles = {};

        for (j = 0; j < jlen; j += 1) {

            source = sources[j];
            href = source.getAttribute("href");
            type = source.getAttribute("type");

            if (href === null) {

                warn(this.bus, "No href defined for source in track '" +
                    trackName + "' in audio element '" + this.name + "'.", asset);

                continue;
            }

            if (type === null) {

                warn(this.bus, "No type defined for source in track '" + 
                    trackName + "' in audio element '" + this.name + "'.", asset);

                continue;
            }

            trackFiles[type] = href;
            trackSettings.urls.push(href);

        }

        this.bus.trigger("wse.assets.loading.increase");

        track = new Howl(trackSettings);

        this.tracks[trackName] = track;
    }

    /**
     * Starts playing the current track.
     * 
     * @param command [XML DOM Element] The command as written in the WebStory.
     * @return [object] Object that determines the next state of the interpreter.
     */
    this.play = function (command) {

        var fadeDuration;

        if (this._playing) {
            return {
                doNext: true
            };
        }

        this._playing = true;
        this._paused = false;

        if (command.getAttribute("fadein")) {

            this.interpreter.waitCounter += 1;
            fadeDuration = +command.getAttribute("fadein");

            this.tracks[this._currentTrack].volume(0);
            this.tracks[this._currentTrack].play();

            this.tracks[this._currentTrack].fade(0, 1, fadeDuration, function () {
                this.interpreter.waitCounter -= 1;
            }.bind(this));
        }
        else {
            this.tracks[this._currentTrack].play();
        }

        return {
            doNext: true
        };
    };

    /**
     * Stops playing the current track.
     * 
     * @param command [XML DOM Element] The command as written in the WebStory.
     * @return [object] Object that determines the next state of the interpreter.
     */
    this.stop = function (command) {

        var fadeDuration;

        if (!this._currentTrack) {
            return {
                doNext: true
            };
        }

        this._playing = false;
        this._paused = false;

        if (command && command.getAttribute("fadeout")) {

            this.interpreter.waitCounter += 1;
            fadeDuration = +command.getAttribute("fadeout");

            this.tracks[this._currentTrack].fade(1, 0, fadeDuration, function () {
                this.tracks[this._currentTrack].stop();
                this.interpreter.waitCounter -= 1;
            }.bind(this));
        }
        else {
            this.tracks[this._currentTrack].stop();
        }

        this.bus.trigger("wse.assets.audio.stop", this);

        return {
            doNext: true
        };
    };

    /**
     * Pauses playing the curren track.
     * 
     * @return [object] Object that determines the next state of the interpreter.
     */
    this.pause = function () {

        if (!this._currentTrack || !this._playing) {
            return {
                doNext: true
            };
        }

        this._paused = true;

        this.tracks[this._currentTrack].pause();

        return {
            doNext: true
        };
    };

    this.bus.trigger("wse.assets.audio.constructor", this);

    this.bus.subscribe("wse.interpreter.restart", function () {
        this.stop();
    }.bind(this));

    window.addEventListener("blur", function () {
        if (this._playing) {
            this.tracks[this._currentTrack].fade(1, 0, 200);
        }
    }.bind(this));

    window.addEventListener("focus", function () {
        if (this._playing) {
            this.tracks[this._currentTrack].fade(0, 1, 200);
        }
    }.bind(this));
}

/**
 * Changes the currently active track.
 * 
 * @param command [DOM Element] The command as specified in the WebStory.
 * @trigger wse.interpreter.warning@interpreter
 * @trigger wse.assets.audio.set@interpreter
 */
Audio.prototype.set = function (command) {

    var wasPlaying = false;

    if (this._playing) {
        wasPlaying = true;
    }

    this.stop();

    this._currentTrack = command.getAttribute("track");

    if (wasPlaying) {
        this.play(command);
    }

    return {
        doNext: true
    };
};

/**
 * Gathers the data to put into a savegame.
 * 
 * @param obj [object] The savegame object.
 */
Audio.prototype.save = function () {

    var obj = {
        currentTrack: this._currentTrack,
        playing: this._playing,
        paused: this._paused
    };

    this.bus.trigger("wse.assets.audio.save", this);

    return obj;
};

/**
 * Restore function for loading the state from a savegame.
 * 
 * @param obj [object] The savegame data.
 * @trigger wse.assets.audio.restore@interpreter
 */
Audio.prototype.restore = function (vals) {

    var key;

    this._playing = vals.playing;
    this._paused = vals.paused;
    this._currentTrack = vals.currentTrack;

    for (key in this.tracks) {
        this.tracks[key].stop();
    }

    if (this._playing && !this._paused) {
        this.tracks[this._currentTrack].play();
    }

    this.bus.trigger("wse.assets.audio.restore", this);
};

module.exports = Audio;

Documents

docs/reference/elements/nametemplate.md
docs/reference/elements/stop.md
docs/development.md
docs/documentation.md
docs/downloads.md
docs/examples.md
docs/games.md
docs/index.md
docs/reference/elements/alert.md
docs/reference/elements/animation.md
docs/reference/elements/assets.md
docs/reference/elements/audio.md
docs/reference/elements/background.md
docs/reference/elements/break.md
docs/reference/elements/character.md
docs/reference/elements/choice.md
docs/reference/elements/clear.md
docs/reference/elements/composite.md
docs/reference/elements/conditionals.md
docs/reference/elements/confirm.md
docs/reference/elements/curtain.md
docs/reference/elements/displayname.md
docs/reference/elements/do.md
docs/reference/elements/easing_attribute.md
docs/reference/elements/else.md
docs/reference/elements/flash.md
docs/reference/elements/flicker.md
docs/reference/elements/fn.md
docs/reference/elements/global.md
docs/reference/elements/globalize.md
docs/reference/elements/goto.md
docs/reference/elements/group.md
docs/reference/elements/hide.md
docs/reference/elements/image.md
docs/reference/elements/imagepack.md
docs/reference/elements/line.md
docs/reference/elements/localize.md
docs/reference/elements/move.md
docs/community.md
docs/reference/elements/option.md
docs/reference/elements/pause.md
docs/reference/elements/play.md
docs/reference/elements/prompt.md
docs/reference/elements/restart.md
docs/reference/elements/scene.md
docs/reference/elements/scenes.md
docs/reference/elements/set.md
docs/reference/elements/set_vars.md
docs/reference/elements/settings.md
docs/reference/elements/shake.md
docs/reference/elements/show.md
docs/reference/elements/source.md
docs/reference/elements/stage.md
docs/reference/elements/start.md
docs/beginners-guide.md
docs/reference/elements/sub.md
docs/reference/elements/tag.md
docs/reference/elements/textbox.md
docs/reference/elements/track.md
docs/reference/elements/transform.md
docs/reference/elements/trigger.md
docs/reference/elements/trigger_command.md
docs/reference/elements/triggers.md
docs/reference/elements/var.md
docs/reference/elements/wait.md
docs/reference/elements/when.md
docs/reference/elements/while.md
docs/reference/elements/with.md
docs/reference/elements/ws.md
docs/reference/elements.md
docs/reference/language.md
docs/reference/structure.md
docs/reference/syntax.md
docs/web-servers.md
libs/MO5/README.md
libs/MO5/libs/using.js/README.md
libs/MO5/js/EventBus.js
libs/MO5/js/Animation.js
libs/MO5/js/CoreObject.js
libs/MO5/js/Exception.js
libs/MO5/js/List.js
libs/MO5/js/MO5.js
libs/MO5/js/Map.js
libs/MO5/js/Point.js
libs/MO5/js/Promise.js
libs/MO5/js/Queue.js
libs/MO5/js/Result.js
libs/MO5/js/Set.js
libs/MO5/js/Size.js
libs/MO5/js/Timer.js
libs/MO5/js/TimerWatcher.js
libs/MO5/js/ajax.js
libs/MO5/js/assert.js
libs/MO5/js/dom.Element.js
libs/MO5/js/dom.effects.typewriter.js
libs/MO5/js/dom.escape.js
libs/MO5/js/easing.js
libs/MO5/js/fail.js
libs/MO5/js/globals.document.js
libs/MO5/js/globals.window.js
libs/MO5/js/range.js
libs/MO5/js/tools.js
libs/MO5/js/transform.js
libs/MO5/js/types.js
libs/MO5/libs/using.js/tests/index.js
libs/MO5/libs/using.js/tests/module1.js
libs/MO5/libs/using.js/tests/module2.js
libs/MO5/libs/using.js/tests/module3.js
libs/MO5/libs/using.js/using.js
libs/MO5/tests/node/EventBus.test.js
libs/MO5/tests/node/Set.test.js
src/savegames.js
src/tools/reveal.js
src/tools/ui.js
src/loader.js
src/tools/tools.js
src/tools/compile.js
src/functions.js
src/DisplayObject.js
src/Game.js
src/Interpreter.js
src/Keys.js
src/LoadingScreen.js
src/Trigger.js
src/assets/Audio.js
src/assets/Background.js
src/assets/Character.js
src/assets/Composite.js
src/assets/Curtain.js
src/assets/Imagepack.js
src/assets/Textbox.js
src/assets.js
src/bus.js
src/commands/alert.js
src/commands/break.js
src/commands/choice.js
src/commands/confirm.js
src/commands/do.js
src/commands/fn.js
src/commands/global.js
src/commands/globalize.js
src/commands/goto.js
src/commands/line.js
src/commands/localize.js
src/commands/prompt.js
src/commands/restart.js
src/commands/set_vars.js
src/commands/sub.js
src/commands/trigger.js
src/commands/var.js
src/commands/wait.js
src/commands/while.js
src/commands/with.js
src/commands.js
src/dataSources/LocalStorage.js
src/dataSources.js
src/engine.js
src/extensions/button.js
src/extensions/colored-rectangle.js
src/extensions/get-backtrace.js
src/extensions/hello.js
src/extensions/side-images.js
CHANGELOG.md
LICENSE.md
README.md
build.js
index.js
index.md
package.js