var easing = require("eases");
var transform = require("transform-js").transform;

var warn = require("../tools/tools").warn;
var DisplayObject = require("../DisplayObject");

/**
 * Constructor function for Composites.
 * 
 * @param asset [DOM Element] The asset definition.
 * @param interpreter [WSE.Interpreter] The interpreter object.
 */
function Composite (asset) {

    var element, children;
    var self, triggerDecreaseFn, width, height;

    this._boxSizeSelectors = ["img"];

    DisplayObject.apply(this, arguments);

    this.cssid = this.cssid || "wse_composite_" + this.name;

    self = this;
    element = this.element;
    width = this.width;
    height = this.height;

    element.setAttribute("class", "asset composite");

    children = asset.getElementsByTagName("image");
    triggerDecreaseFn =
        self.bus.trigger.bind(self.bus, "wse.assets.loading.decrease", null, false);

    [].forEach.call(children, function (current) {

        var tags, src, image;

        tags = current.getAttribute("tags");
        src = current.getAttribute("src");

        if (tags === null) {
            warn(self.bus, "Image without tags in composite '" + self.name + "'.", asset);
            return;
        }

        if (src === null) {
            warn(self.bus, "Image without src in composite '" + self.name + "'.", asset);
            return;
        }

        image = new Image();

        self.bus.trigger("wse.assets.loading.increase", null, false);
        image.addEventListener('load', triggerDecreaseFn);

        image.src = src;
        image.style.opacity = 0;
        image.style.position = "absolute";
        image.draggable = false;

        image.setAttribute("data-wse-tags", tags);

        if (width !== null) {
            image.setAttribute('width', width);
        }

        if (height !== null) {
            image.setAttribute('height', height);
        }

        element.appendChild(image);

    });

    this.current = [];
}

Composite.prototype = Object.create(DisplayObject.prototype);

Composite.prototype.tag = function (command, args) {

    var self, old, duration, isAnimation, bus = this.bus, element;
    var toAdd, toRemove, imagesByTags, oldImages, newImages;

    args = args || {};
    self = this;
    toAdd = extractTags(command.getAttribute("add") || "");
    toRemove = extractTags(command.getAttribute("remove") || "");
    duration = command.getAttribute("duration") || 400;
    isAnimation = args.animation === true ? true : false;

    if (!toAdd.length && !toRemove.length) {

        warn(bus, "No attribute 'add' or 'remove' on element " +
            "referencing composite '" + this.name + "'. Expected at least one.", command);

        return {
            doNext: true
        };
    }

    old = this.current;

    if (toRemove.length && toRemove[0] === "*") {
        this.current = toAdd.slice();
    }
    else {

        this.current = old.filter(function (tag) {
            return toRemove.indexOf(tag) < 0;
        });

        toAdd.forEach(function (tag) {
            if (self.current.indexOf(tag) < 0) {
                self.current.push(tag);
            }
        });
    }

    imagesByTags = getImagesByTags(this);
    oldImages = [];
    newImages = [];

    old.forEach(function (tag) {
        if (imagesByTags[tag]) {
            imagesByTags[tag].forEach(function (image) {
                if (oldImages.indexOf(image) < 0) {
                    oldImages.push(image);
                }
            });
        }
    });

    this.current.forEach(function (tag) {
        if (imagesByTags[tag]) {
            imagesByTags[tag].forEach(function (image) {
                if (newImages.indexOf(image) < 0) {
                    newImages.push(image);
                }
            });
        }
    });

    newImages = newImages.filter(function (image) {

        if (oldImages.indexOf(image) >= 0) {
            oldImages.splice(oldImages.indexOf(image), 1);
            return false;
        }

        return true;
    });

    if (!isAnimation) {
        self.interpreter.waitCounter += 1;
    }

    element = document.getElementById(this.cssid);
    element.style.width = highest(newImages, "offsetWidth") + "px";
    element.style.height = highest(newImages, "offsetHeight") + "px";

    (function () {

        var valFn, finishFn, options;

        valFn = function (v) {
            newImages.forEach(function (image) {
                image.style.opacity = v;
            });
        };

        finishFn = function () {

            if (!isAnimation) {
                self.interpreter.waitCounter -= 1;
            }
        };

        options = {
            duration: duration,
            easing: easing.cubicOut
        };

        transform(0, 1, valFn, options, finishFn);
    }());

    if (this.current !== null) {

        if (!isAnimation) {
            self.interpreter.waitCounter += 1;
        }

        (function () {

            function timeoutFn () {

                var options; 

                function valFn (v) {
                    oldImages.forEach(function (image) {
                        image.style.opacity = v;
                    });
                }

                function finishFn () {
                    if (!isAnimation) {
                        self.interpreter.waitCounter -= 1;
                    }
                }

                options = {
                    duration: duration,
                    easing: easing.cubicIn
                };

                transform(1, 0, valFn, options, finishFn);
            }

            timeoutFn();
        }());
    }

    return {
        doNext: true
    };
};

Composite.prototype.save = function () {

    var cur, obj;

    cur = this.current || [];

    obj = {
        assetType: "Composite",
        current: cur,
        cssid: this.cssid,
        xAnchor: this.xAnchor,
        yAnchor: this.yAnchor,
        z: this.z
    };

    this.bus.trigger(
        "wse.assets.composite.save",
        {
            subject: this,
            saves: obj
        }
    );

    return obj;
};

Composite.prototype.restore = function (save) {

    this.cssid = save.cssid;
    this.z = save.z;
    this.current = save.current.slice();
    this.xAnchor = save.xAnchor;
    this.yAnchor = save.yAnchor;

    this.element = document.getElementById(this.cssid);
    this.element.style.zIndex = this.z;

    this.bus.trigger(
        "wse.assets.composite.restore",
        {
            subject: this,
            saves: save
        }
    );
};

function getImagesByTags (self) {

    var images, imagesByTag;

    images = document.getElementById(self.cssid).getElementsByTagName("img");
    imagesByTag = {};

    [].forEach.call(images, function (image) {

        var tags = extractTags(image.getAttribute("data-wse-tags") || "");

        tags.forEach(function (tag) {

            if (!Array.isArray(imagesByTag[tag])) {
                imagesByTag[tag] = [];
            }

            imagesByTag[tag].push(image);
        });
    });

    return imagesByTag;
}

function extractTags (raw) {
    return raw.split(",").map(function (rawTag) {
        return rawTag.trim();
    });
}

function highest (all, key) {

    var biggest = 0;

    all.forEach(function (item) {
        if (item[key] > biggest) {
            biggest = item[key];
        }
    });

    return biggest;
}

module.exports = Composite;

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