
//handles the level, this is a global class
//that contains the running level
//as initialized with init
ig.module('game.level')
.requires(
    'impact.impact',
    'game.floor',
    'game.utils',
    'game.movement'
)
.defines(function () {
    "use strict"
     ig.global.Level = function () {
        var floors = [];
        var numBrokenWindows = 0;
        var getWindow = function (position) {
            return floors[position.floor].windows[position.window];
        };
        //get the actual position that corosponds to a specific index
        var getPosition = function () {
            var index = {};
            //handle the case where we passed in two
            //values instead of a object
            if (arguments.length === 2) {
                index.floor = arguments[0];
                index.window = arguments[1];
            } else {
                index = arguments[0];
            }
            var retval = this.floors[index.floor].getPlayerPosition(index.window);

            return retval;
        };
        function getIndex(position, direction) {
            var directions = Utils.directionsEnum;
            //COPY the value of the original index
            var retval = { floor: position.floor, window: position.window };
            switch (direction) {
                case directions.UP:
                    retval.floor++;
                    break;
                case directions.DOWN:
                    retval.floor--;
                    break;
                case directions.LEFT:
                    retval.window--;
                    break;
                case directions.RIGHT:
                    retval.window++;
                    break;
                default:
                    throw new Error("Invalid Direction: " + direction);
            }
            return retval;
        };
        //move to a new index in the level grid, does not deal
        //with actual positions
        var getNewIndex = function (originalIndex, direction, settings) {
            var nocollide = false;
            if (settings !== undefined) {
                nocollide = !settings.checkCollisions;
            }
            var retval = getIndex(originalIndex, direction);
            //check to see if we are out of bounds and if so dont actually move
            //the last argument logical statement here may by out of bounds but
            //due to short-circuit eval it will never get hit if it is
            if (retval.floor < 0 || retval.floor >= floors.length || retval.window < 0 || retval.window >= floors[retval.floor].windows.length) {
                return originalIndex;
            }
            
            //check to see if there is an obstacle in the way and if so dont move
            if (!nocollide) {
                if (checkCollisions(originalIndex, direction)) {
                    return originalIndex;
                }
            }
            return retval;
        };
        //checks to see if there is something
        //preventing us from moving in direction
        //if there is return it, otherwise return false
        function checkCollisions(position, direction) {
            var originalWindow = getWindow(position);
            var newIndex = getIndex(position,direction);
            if(direction === Utils.directionsEnum.UP && floors[position.floor].getLedge1() !== undefined) {
                return true;
            }
            var newWindow = getWindow(newIndex);
            if ((originalWindow.obstacles && direction in originalWindow.obstacles) ||
                (newWindow.obstacles && direction.opposite in newWindow.obstacles)) {

                //differentiate between the two hit cases and start the approperate obstacle moving
                if ((originalWindow.obstacles && direction in originalWindow.obstacles)) {
                    originalWindow.obstacles[direction].handleHit(direction.toString());
                    return originalWindow.obstacles[direction];
                } else if (newWindow.obstacles && (direction.opposite in newWindow.obstacles)) {
                    newWindow.obstacles[direction.opposite].handleHit(direction.toString());
                    return newWindow.obstacles[direction.opposite];
                }
                return true;
            }
            return false;

        };
        //When we break a window we want
        //to increment the count of broken
        //windows
        function handlebreak() {
            numBrokenWindows++;
        };

        //as above when we fix a window we want to deincrement that count
        function handlefix() {
            numBrokenWindows--;
        };
        //remove this level and all the floors and windows
        //contained in it
        function removeLevel() {
            for (var i = 0; i < floors.length; ++i) {
                floors[i].removeFloor();
            }
            if (this.duckSpawner) {
                //get rid of the duck spawner if we have one
                this.duckSpawner.kill();
            }
            if (this.pieSpanwer) {
                this.pieSpawner.kill();
            }
        };
        function pos() {
            return {
                x: floors[0].getBuildingSegment().pos.x,
                y: floors[0].getBuildingSegment().pos.y + floors[0].getBuildingSegment().size.y
            };
        };

        //set a property on this level and all the entities in it
        //this is so that during level transitions we can change the
        //velocity of a level as a whole
        function setProperty(prop, newVal) {
            for (var i = 0; i < floors.length; ++i) {
                for (var j = 0; j < floors[i].windows.length; ++j) {
                    var curwin = floors[i].windows[j];
                    curwin[prop] = newVal;
                    floors[i].windows[j].setObstacleProp(prop, newVal);
                }

                if (floors[i].getLedge1() !== undefined && floors[i].getLedge2() !== undefined) {
                    floors[i].getLedge1()[prop] = newVal;
                    floors[i].getLedge2()[prop] = newVal;
                }
                floors[i].getBuildingSegment()[prop] = newVal;
            }

        };
        function squashWindows() {
            var retval = [];
            for(var i = 0; i < floors.length; ++i) {
                for(var j = 0; j < floors[i].windows.length; ++j) {
                    retval.push(floors[i].windows[j]);
                }
            }
            return retval;
        };
        function setBrokenWindows(num) {
            numBrokenWindows = num;
        };
        function reposition() {
            for(var i = 0; i < floors.length; ++i) {
                floors[i].reposition();
            }
        };
        function zeroOffset() {
            for(var i = 0; i < floors.length; ++i) {
                floors[i].reposition();
            }
        };
        //updates the offsets of all floors in this level,
        //note that if changing the size of the canvas this
        //should be called before the canvas is actually
        //resized
        function updateOffset() {
            for(var i = 0; i < floors.length; ++i) {
                floors[i].updateOffset();
            }
        };
        //public functions and whatnot
        this.updateOffset = updateOffset;
        this.zeroOffset = zeroOffset;
        this.reposition = reposition;
        this.pos = pos;
        this.squashWindows = squashWindows;
        this.handleFix = handlefix;
        this.handleBreak = handlebreak;
        this.brokenWindows = function () { return numBrokenWindows };
        this.setBrokenWindows = setBrokenWindows;
        this.floors = floors;
        this.getWindow = getWindow;
        this.getNewIndex = getNewIndex;
        this.getPosition = getPosition;
        this.setProperty = setProperty;
        this.removeLevel = removeLevel;
        
    };
    //LANGFEATURE: this is an iterator
    ig.global.LevelIterator = function (level) {
        this.level = level;
        this.currentFloor = 0;
        this.currentWindow = 0;
        this.maxFloor = this.level.floors.length;
        this.maxWindow = this.level.floors[this.maxFloor - 1].windows.length;
        this.next = function () {

            if (this.currentFloor >= this.maxFloor) {
                throw StopIteration;
            } else {
                var retval = this.level.floors[this.currentFloor].windows[this.currentWindow];
                if (this.currentWindow < this.maxWindow - 1) {
                    ++this.currentWindow;
                } else {
                    ++this.currentFloor;
                    this.currentWindow = 0;
                }
                return retval;
            }
        }
    };
    Level.prototype.__iterator__ = function () {
        return new LevelIterator(this);
    };
});
