/* eslint no-console: off */

var ajax = require("easy-ajax");
var DataBus = require("databus");

var tools = require("./tools/tools");
var loader = require("./loader");
var Keys = require("./Keys");
var Interpreter = require("./Interpreter");
var bus = require("./bus");

var truthy = tools.truthy;

/**
 * Constructor used to create instances of games.
 * 
 * You can configure the game instance using the config object
 * that can be supplied as a parameter to the constructor function.
 * 
 * The options are:
 *  - url: [string] The URL of the WebStory file.
 *  - debug: [bool] Should the game be run in debug mode? (not used yet)
 *  - host: [object] The HOST object for the LocalContainer 
 *      version. Optional.
 * 
 * @event wse.game.constructor@WSE.bus
 * @param args A config object. See above for details.
 */
function Game (args) {

    var host;

    bus.trigger("wse.game.constructor", {args: args, game: this});

    args = args || {};
    this.bus = new DataBus();
    this.url = args.url || "game.xml";
    this.gameId = args.gameId || null;
    this.ws = null;
    this.debug = args.debug === true ? true : false;

    host = args.host || false;
    this.host = host;

    if (this.gameId) {
        loader.generateFromString(
            document.getElementById(this.gameId).innerHTML,
            this.load.bind(this)
        );
    }
    else {
        if (host) {
            loader.generateFromString(host.get(this.url), this.load.bind(this));
        }
        else {
            loader.generateGameFile(this.url, this.load.bind(this));
        }
    }

    this.interpreter = new Interpreter(this, {
        datasource: args.datasource
    });

    this.keys = new Keys();
    this.listenersSubscribed = false;
    //console.log("this.interpreter: ", this.interpreter);

    this.bus.subscribe(
        function (data) {
            console.log("Message: " + data);
        }, 
        "wse.interpreter.message"
    );

    this.bus.subscribe(
        function (data) {
            console.log("Error: " + data.message);
        }, 
        "wse.interpreter.error"
    );

    this.bus.subscribe(
        function (data) {
            console.log("Warning: " + data.message, data.element);
        }, 
        "wse.interpreter.warning"
    );
}

/**
 * Loads the WebStory file using the AJAX function and triggers
 * the game initialization.
 */
Game.prototype.load = function (gameDocument) {
    this.ws = gameDocument;
    this.init();
};

Game.prototype.loadFromUrl = function (url) {

    //console.log("Loading game file...");
    var fn, self;

    this.url = url || this.url;

    self = this;

    fn = function (obj) {
        self.ws = obj.responseXML;
        //console.log("Response XML: " + obj.responseXML);
        self.init();
    };

    ajax.get(this.url, null, fn);

};

/**
 * Initializes the game instance.
 */
Game.prototype.init = function () {

    var ws, stage, stageElements, stageInfo, width, height, id, alignFn, resizeFn;

    ws = this.ws;

    (function () {

        var parseErrors = ws.getElementsByTagName("parsererror");

        console.log("parsererror:", parseErrors);

        if (parseErrors.length) {
            document.body.innerHTML = "" +
                '<div class="parseError">'+
                    "<h1>Cannot parse WebStory file!</h3>" +
                    "<p>Your WebStory file is mal-formed XML and contains these errors:</p>" +
                    '<pre class="errors">' + parseErrors[0].innerHTML + '</pre>' +
                '</div>';
            throw new Error("Can't parse game file, not well-formed XML:", parseErrors[0]);
        }
    }());

    try {
        stageElements = ws.getElementsByTagName("stage");
    }
    catch (e) {
        console.log(e);
    }

    width = "800px";
    height = "480px";
    id = "Stage";

    if (!stageElements || stageElements.length < 1) {
        throw new Error("No stage definition found!");
    }

    stageInfo = stageElements[0];
    width = stageInfo.getAttribute("width") || width;
    height = stageInfo.getAttribute("height") || height;
    id = stageInfo.getAttribute("id") || id;

    // Create the stage element or inject into existing one?
    if (stageInfo.getAttribute("create") === "yes") {
        stage = document.createElement("div");
        stage.setAttribute("id", id);
        document.body.appendChild(stage);
    }
    else {
        stage = document.getElementById(id);
    }

    stage.setAttribute("class", "WSEStage");

    stage.style.width = width;
    stage.style.height = height;

    // Aligns the stage to be always in the center of the browser window.
    // Must be specified in the game file.
    alignFn = function () {

        var dim = tools.getWindowDimensions();

        stage.style.left = (dim.width / 2) - (parseInt(width, 10) / 2) + 'px';
        stage.style.top = (dim.height / 2) - (parseInt(height, 10) / 2) + 'px';
    };

    if (stageInfo.getAttribute("center") === "yes") {
        window.addEventListener('resize', alignFn);
        alignFn();
    }

    // Resizes the stage to fit the browser window dimensions. Must be
    // specified in the game file.
    resizeFn = function () {
        console.log("Resizing...");
        tools.fitToWindow(stage, parseInt(width, 10), parseInt(height, 10));
    };

    if (stageInfo.getAttribute("resize") === "yes") {
        window.addEventListener('resize', resizeFn);
        resizeFn();
    }

    this.stage = stage;
    //     stage.onclick = function() { self.interpreter.next(); };

    this.applySettings();

    // This section only applies when the engine is used inside
    // the local container app.
    if (this.host) {

        this.host.window.width = parseInt(width, 10);
        this.host.window.height = parseInt(height, 10);

        (function (self) {

            var doResize = truthy(self.getSetting("host.stage.resize"));

            if (!doResize) {
                return;
            }

            window.addEventListener("resize", function () {
                console.log("Resizing...");
                tools.fitToWindow(stage, parseInt(width, 10), parseInt(height, 10));
            });
        }(this));
    }
};

/**
 * Returns the value of a setting as specified in the WebStory file.
 * @param name [string] The name of the setting.
 * @return [mixed] The value of the setting or null.
 */
Game.prototype.getSetting = function (name) {

    var ret, settings, i, len, cur, curName;

    settings = this.ws.getElementsByTagName("setting");

    for (i = 0, len = settings.length; i < len; i += 1) {

        cur = settings[i];
        curName = cur.getAttribute("name") || null;

        if (curName !== null && curName === name) {
            ret = cur.getAttribute("value") || null;
            return ret;
        }
    }

    return null;
};

FIXME: implement...

Game.prototype.applySettings = function () {

    this.webInspectorEnabled = truthy(this.getSetting("host.inspector.enable"));

    if (this.host) {

        if (this.webInspectorEnabled === true) {
            this.host.inspector.show();
        }
    }
};

/**
 * Use this method to start the game. The WebStory file must have
 * been successfully loaded for this to work.
 */
Game.prototype.start = function () {

    var fn, contextmenu_proxy, self;

    self = this;

    if (this.ws === null) {

        return setTimeout(
            function () {
                self.start();
            }
        );
    }

    // Listener that sets the interpreter's state machine to the next state
    // if the current state is not pause or wait mode.
    // This function gets executed when a user clicks on the stage.
    fn = function () {

        if (self.interpreter.state === "pause" || self.interpreter.waitCounter > 0) {
            return;
        }

        //console.log("Next triggered by user...");
        self.interpreter.next(true);
    };

    contextmenu_proxy = function (e) {

        self.bus.trigger("contextmenu", {});

        // let's try to prevent real context menu showing
        if (e && typeof e.preventDefault === "function") {
            e.preventDefault();
        }

        return false;
    };

    this.subscribeListeners = function () {

        this.stage.addEventListener('contextmenu', contextmenu_proxy);
        this.stage.addEventListener('click', fn);

        this.listenersSubscribed = true;
    };

    this.unsubscribeListeners = function () {

        this.stage.removeEventListener('contextmenu', contextmenu_proxy);
        this.stage.removeEventListener('click', fn);

        this.listenersSubscribed = false;
    };

    this.interpreter.start();
};

module.exports = Game;

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