var each = require("enjoy-core/each");
var typechecks = require("enjoy-typechecks");

var engine = require("./engine");
var tools = require("./tools/tools");

var isNull = typechecks.isNull;
var isUndefined = typechecks.isUndefined;

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

function load (interpreter, name) {

    var ds, savegame, scene, sceneId, scenePath, scenes;
    var savegameId, bus = interpreter.bus;

    savegameId = buildSavegameId(interpreter, name);
    ds = interpreter.datasource;
    savegame = ds.get(savegameId);

    bus.trigger(
        "wse.interpreter.load.before",
        {
            interpreter: interpreter,
            savegame: savegame
        }, 
        false
    );

    if (!savegame) {
        warn(bus, "Could not load savegame '" + savegameId + "'!");
        return false;
    }

    savegame = JSON.parse(savegame);
    interpreter.stage.innerHTML = savegame.screenContents;

    restoreSavegame(interpreter, savegame.saves);

    interpreter.startTime = savegame.startTime;
    interpreter.runVars = savegame.runVars;
    interpreter.log = savegame.log;
    interpreter.visitedScenes = savegame.visitedScenes;
    interpreter.index = savegame.index;
    interpreter.wait = savegame.wait;
    interpreter.waitForTimer = savegame.waitForTimer;
    interpreter.currentElement = savegame.currentElement;
    interpreter.callStack = savegame.callStack;
    interpreter.waitCounter = savegame.waitCounter;
    interpreter.state = "listen";

    sceneId = savegame.sceneId;
    interpreter.sceneId = sceneId;

    scenes = interpreter.story.getElementsByTagName("scene");
    interpreter.scenes = scenes;

    scene = find(function (scene) {
        return scene.getAttribute("id") === sceneId;
    }, interpreter.scenes);

    if (!scene) {

        bus.trigger(
            "wse.interpreter.error",
            {
                message: "Loading savegame '" + savegameId + "' failed: Scene not found!"
            }
        );

        return false;
    }

    scenePath = savegame.scenePath;
    interpreter.scenePath = scenePath.slice();

    interpreter.currentCommands = scene.childNodes;

    while (scenePath.length > 0) {
        interpreter.currentCommands = interpreter.currentCommands[scenePath.shift()].childNodes;
    }

    // Re-insert choice menu to get back the DOM events associated with it:
    // Remove savegame menu on load:
    (function (interpreter) {

        var index, wseType, com, rem;

        each(function (cur) {

            if (isUndefined(cur) || isNull(cur)) {
                return;
            }

            wseType = cur.getAttribute("data-wse-type") || "";
            rem = truthy(cur.getAttribute("data-wse-remove"));

            if (rem === true) {
                interpreter.stage.removeChild(cur);
            }

            if (wseType !== "choice") {
                return;
            }

            index = parseInt(cur.getAttribute("data-wse-index"), 10) || null;

            if (index === null) {
                warn(interpreter.bus, "No data-wse-index found on element.");
                return;
            }

            com = interpreter.currentCommands[index];

            if (com.nodeName === "#text" || com.nodeName === "#comment") {
                return;
            }

            interpreter.stage.removeChild(cur);
            engine.commands.choice(com, interpreter);
            interpreter.waitCounter -= 1;

        }, interpreter.stage.getElementsByTagName("*"));

    }(interpreter));

    bus.trigger(
        "wse.interpreter.load.after",
        {
            interpreter: interpreter,
            savegame: savegame
        }, 
        false
    );

    return true;
}

function save (interpreter, name) {

    name = name || "no name";

    var savegame, json, key, savegameList, listKey, lastKey, bus = interpreter.bus;

    savegame = {};

    bus.trigger(
        "wse.interpreter.save.before",
        {
            interpreter: interpreter,
            savegame: savegame
        }, 
        false
    );

    savegame.saves = createSavegame(interpreter);
    savegame.startTime = interpreter.startTime;
    savegame.saveTime = Math.round(Date.now() / 1000);
    savegame.screenContents = interpreter.stage.innerHTML;
    savegame.runVars = interpreter.runVars;
    savegame.name = name;
    savegame.log = interpreter.log;
    savegame.visitedScenes = interpreter.visitedScenes;
    savegame.gameUrl = interpreter.game.url;
    savegame.index = interpreter.index;
    savegame.wait = interpreter.wait;
    savegame.waitForTimer = interpreter.waitForTimer;
    savegame.currentElement = interpreter.currentElement;
    savegame.sceneId = interpreter.sceneId;
    savegame.scenePath = interpreter.scenePath;
    savegame.listenersSubscribed = interpreter.game.listenersSubscribed;
    savegame.callStack = interpreter.callStack;
    savegame.waitCounter = interpreter.waitCounter;
    savegame.pathname = location.pathname;

    key = buildSavegameId(interpreter, name);

    json = JSON.stringify(savegame);

    listKey = "wse_" + savegame.pathname + "_" + savegame.gameUrl + "_savegames_list";

    savegameList = JSON.parse(interpreter.datasource.get(listKey));
    savegameList = savegameList || [];
    lastKey = savegameList.indexOf(key);

    if (lastKey >= 0) {
        savegameList.splice(lastKey, 1);
    }

    savegameList.push(key);

    try {
        interpreter.datasource.set(key, json);
        interpreter.datasource.set(listKey, JSON.stringify(savegameList));
    }
    catch (e) {

        warn(bus, "Savegame could not be created!");

        bus.trigger(
            "wse.interpreter.save.after.error",
            {
                interpreter: interpreter,
                savegame: savegame
            }, 
            false
        );

        return false;
    }

    bus.trigger(
        "wse.interpreter.save.after.success",
        {
            interpreter: interpreter,
            savegame: savegame
        }
    );

    return true;
}

function createSavegame (interpreter) {

    var saves = {};

    each(function (asset, key) {

        try {
            saves[key] = asset.save();
        }
        catch (e) {
            console.error("WSE Internal Error: Asset '" + key + 
                "' does not have a 'save' method!");
        }

    }, interpreter.assets);

    return saves;
}

function restoreSavegame (interpreter, saves) {

    each(function (asset, key) {

        try {
            asset.restore(saves[key]);
        }
        catch (e) {
            console.error(e);
            warn(interpreter.bus, "Could not restore asset state for asset '" + key + "'!");
        }

    }, interpreter.assets);

}

function buildSavegameId (interpreter, name) {

    var vars = {};

    vars.name = name;
    vars.id = "wse_" + location.pathname + "_" + interpreter.game.url + "_savegame_" + name;

    interpreter.bus.trigger(
        "wse.interpreter.save.before",
        {
            interpreter: interpreter,
            vars: vars
        }, 
        false
    );

    return vars.id;
}

function getSavegameList (interpreter, reversed) {

    var names;
    var out = [];
    var key = "wse_" + location.pathname + "_" + interpreter.game.url + "_savegames_list";
    var json = interpreter.datasource.get(key);

    if (json === null) {
        return out;
    }

    names = JSON.parse(json);
    out = [];

    each(function (name) {

        if (reversed === true) {
            out.unshift(JSON.parse(interpreter.datasource.get(name)));
        }
        else {
            out.push(JSON.parse(interpreter.datasource.get(name)));
        }

    }, names);

    interpreter.bus.trigger(
        "wse.interpreter.getsavegamelist",
        {
            interpreter: interpreter,
            list: out,
            names: names
        }, 
        false
    );

    return out;
}

function remove (interpreter, name) {

    var sgs, key, index, json, id;

    key = "wse_" + location.pathname + "_" + interpreter.game.url + "_savegames_list";
    json = interpreter.datasource.get(key);

    if (json === null) {
        return false;
    }

    sgs = JSON.parse(json);
    id = buildSavegameId(interpreter, name);
    index = sgs.indexOf(id);

    if (index >= 0) {

        sgs.splice(index, 1);

        interpreter.datasource.set(
            "wse_" + location.pathname + "_" + interpreter.game.url + "_savegames_list",
            JSON.stringify(sgs)
        );

        interpreter.datasource.remove(id);

        return true;
    }

    return false;
}

module.exports = {
    save: save,
    load: load,
    remove: remove,
    getSavegameList: getSavegameList
};

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