ig.module(
    'game.entities.window'
)
.requires(
    'impact.input',
    'impact.entity',
    'impact.animation',
    'game.entities.obstacle',
    'game.utils',
    'game.config',
    'game.effects',
    'game.entities.head'
)
.defines(function () {
    "use strict"
    ig.global.EntityWindow = ig.Entity.extend({
        zIndex: -1,
        color: "Green",
        size: { x: IMG.SIZE(125), y: IMG.SIZE(215) },
        gravityFactor: 0,
        onFixed: null,
        onBreak: null,
        finalAnim: null,
        hasInitialized: false,
        maxVel: { x: 0, y: 500 },
        vel: { x: 0, y: 0 },
        init: function (x, y, settings) {
            this.parent(x, y, settings);
        },

        //add an animation for fixing a window,
        //transition from the from animation to
        //the to animation and blink a number of times
        //specified by blinks
        //from and to are both Animation objects
        addFixAnim: function (from, to, blinks) {
            var sequence = [];
            for (var i = 0; i < blinks; ++i) {
                sequence.push(this.anims[from].tile);
                sequence.push(this.anims[to].tile);

            }
            this.addAnim('fix' + from, 0.05, sequence, true);
            this.anims['fix' + from].dest = to;
        },
        removeWinodw: function () {
            if (this.obstacles) {
                for (var attr in this.obstacles) {
                    if (this.obstacles[attr].kill) {
                        this.obstacles[attr].kill();
                    }
                }
            }
            this.removeHead();
            this.kill();
        },
        //gets the string representation of the current animation
        //there may well be a better way to do this
        getCurrentAnimName: function () {
            return this.getAnimName(this.currentAnim);
        },
        getFinalAnimName: function () {
            return this.getAnimName(this.finalAnim);
        },
        getAnimName: function (anim) {
            for (var i in this.anims) {
                if (this.anims[i] === anim) {
                    return i.toString();
                }
            }
            return undefined;
        },
        //returns true if we are in the process of repairing the window
        //and false if we are not
        isRepairing: function () {
            if (this.currentAnim.dest) {
                return true;
            } else {
                return false;
            }
        },
        //repair the window by selecting animation that begins with fix and
        //ends with the current state of the window
        repairWindow: function () {
            //we dont want to try and repair while we are already repairing
            if (!this.currentAnim.dest && !this.isFixed()) {
                this.currentAnim = this.anims['fix' + this.getCurrentAnimName()].rewind();
                if (this.currentAnim.dest.rewind) {
                    this.currentAnim.dest = this.currentAnim.dest.rewind();
                }
                ig.game.playWindowFixSound();
            }
        },

        //breaks the window one state at a time
        breakWindow: function () {
            //var alreadyBroke = false;
            if (this.currentAnim === this.anims.fixed) {
                //if we are fixed then progress to the topBroken state
                this.currentAnim = this.anims.topBroken.rewind();
                if (this.onBreak !== undefined) {
                    this.onBreak();
                }

            } else if (this.currentAnim === this.anims.topBroken ||
                        this.currentAnim === this.anims.bottomBroken) {
                //alreadyBroke = true;
                //if we already have one paine broken then progress to the both
                //broken version
                this.currentAnim = this.anims.bothBroken.rewind();
            }


            Effects.windowBreak(this.pos);

            this.hasInitialized = true;

        },
        //fully break the window
        breakFully: function () {
            if (this.finalAnim !== this.currentAnim) {
                this.currentAnim = this.finalAnim;
                this.hasInitialized = true;
                Effects.windowBreak(this.pos);
                return true;
            }
            return false;
        },
        //returns true if the window is fixed and false if it is
        //not fixed
        isFixed: function () {
            var fixedStates = ["fixed", "bottomOpen", "unbreakable"];
            var retval = fixedStates.some(function (val) {
                var hasInit = this.hasInitialized;
                var finalAnim = this.getFinalAnimName();
                var currentAnim = this.getCurrentAnimName();
                return (!hasInit && finalAnim === val) || (hasInit && currentAnim === val);
            }, this);
            return retval;
        },


        //spawns an obstacle facing in the direction specified
        spawnObstacle: function (direction) {
            if (!this.obstacles) {
                this.obstacles = [];
            }

            //and herin lies the power of type Coercion and javascript's object system
            this.obstacles[direction] = ig.game.spawnEntity(EntityObstacle, this.pos.x, this.pos.y, { color: this.color });
            this.obstacles[direction].currentAnim = this.obstacles[direction].anims[direction];


        },

        //moves the obstacles that are attached to this window
        //into the windows new position
        moveObstacles: function () {
            if ("obstacles" in this) {
                for (var attr in this.obstacles) {
                    if ("pos" in this.obstacles[attr]) {
                        this.obstacles[attr].pos.x = this.pos.x;
                        this.obstacles[attr].pos.y = this.pos.y;
                    }
                }
            }
        },

        //takes an array of booleans specifying in which directions to spawn obstacles
        applyObstacleMask: function (mask) {
            for (var i = 0; i < mask.length; ++i) {
                if (mask[i]) {
                    this.spawnObstacle(i);
                }
            }
        },
        //go through our obstacles and set
        //a proprety of them to the given value
        //good for changine stuff about the whole level
        setObstacleProp: function (prop, value) {
            if (this.obstacles) {
                for (var attr in this.obstacles) {
                    if (prop in this.obstacles[attr]) {
                        this.obstacles[attr][prop] = value;
                    }
                }
            }
        },
        getPlayerPosition: function () {
            var retval = {
                x: this.pos.x,
                y: this.pos.y - IMG.SIZE(15)
            };
            return retval;
        },
        openWindow: function () {
            if (this.anims.bottomOpen) {
                this.currentAnim = this.anims.bottomOpen;
                return true;
            } else {
                return false;
            }
        },
        closeWindow: function () {
            if ((this.anims.bottomOpen) && this.currentAnim === this.anims.bottomOpen) {
                this.currentAnim = this.anims.fixed;
                return true;
            } else {
                return false;
            }
        },
        spawnHead: function () {
            var headEnt = ig.game.spawnEntity(EntityHead, this.pos.x, this.pos.y);
            this.head = headEnt;
        },
        removeHead: function () {
            if (this.head) {
                this.head.kill();
                delete this.head;
                return true;
            } else {
                return false;
            }
        },
        update: function () {
            if (this.currentAnim.dest && this.currentAnim.frame === this.currentAnim.sequence.length - 1) {
                this.currentAnim = this.anims[this.currentAnim.dest];
                //call onfixed only when we are DONE fixing a window
                //prevents the player from spamming fix
                if (this.getCurrentAnimName() === "fixed") {
                    if (this.onFixed !== null) {
                        this.onFixed();
                    }
                }

            }
            //this.parent();
            if (this.isRepairing() || this.vel.x !== 0 || this.vel.y !== 0) {
                //this call is here since we can
                //skip updating when we are not actually changing animations,
                //that way most of the time we just sit around
                this.parent();
            }

        }

    });

});
