
//stores a path and allows following that path
//a pathe essentially cnosists of a sequence of movements
//and the ability to move from one to another when needed
ig.module('game.path')
.requires(
    'game.movement',
    'game.movement-style'
)
.defines(function () {
    "use strict";
    ig.global.PathBase = ig.Class.extend({
        start: null,
        points: null,
        movements: [],
        currentMovement: 0,
        init: function (points, speed, styles) {
            this.start = points[0];
            this.points = points;
            this.speed = speed;
            this.styles = styles;
            this.constructMovements();
        },
        constructMovements: function () {
            this.movements = [];
            for (var i = 1; i < this.points.length; ++i) {
                var newStyle;
                if (this.styles) {
                    newStyle = this.styles[i - 1];
                } else {
                    newStyle = MovementStyle.linear;
                }
                this.movements.push(new Movement(this.points[i - 1], this.points[i], this.speed, newStyle));
            }
        },
        isDone: function () {
            if (this.currentMovement < this.movements.length) {
                return false;
            } else {
                return true;
            }
        },
        updateCurrentMovement: function () {
            if (!this.isDone() && this.movements[this.currentMovement].isDone()) {
                this.currentMovement++;
            }
        },
        //function to simplify following paths
        getCurrentPosition: function () {
            
            var retval = this.movements[this.currentMovement].getPosition();
            this.updateCurrentMovement();
            return retval;
        },
        getUpdatePosition: function (ent) {
            var newPos = this.getCurrentPosition();
            
            if (Math.abs(ent.pos.x - newPos.x) > 25 || Math.abs(ent.pos.y - newPos.y) > 25) {
                var xdif = ent.pos.x - newPos.x;
                var ydif = ent.pos.y - newPos.y;
                for (var i = 0; i < this.points.length; ++i) {
                    this.points[i].x += xdif;
                    this.points[i].y += ydif;
                }
                this.constructMovements();
            }
            
            return newPos;
        }
    });
    ig.global.Path = PathBase.extend({
        start: undefined,
        path: undefined,
        speed: undefined,
        windowsCache: [],
        cacheValid: false,
        init: function (startingLocation, path, speed) {
            this.start = startingLocation;
            this.path = path;
            var endGrid;
            this.constructPath(startingLocation, path, speed);
            this.currentDirection = function () { return path[this.currentMovement]; };
            this.speed = speed || 0.5;
        },

        //get an array of all references to all the window
        //entities on this path
        getWindowsOnPath: function () {
            
            var retval = [];
            for (var i = 0; i < this.gridPath.length; ++i) {
                retval.push(ig.game.level.getWindow(this.gridPath[i]));
            }
            return retval;
        },
        //gets the position like the getPosition function but
        //in addition it checks if the passed entities position
        //is too far from whre it needs to be and if so updates the path
        getUpdatePosition: function (ent) {
            var newPos = this.getCurrentPosition();
            if (Math.abs(ent.pos.x - newPos.x) > 25 || Math.abs(ent.pos.y - newPos.y) > 25) {
                this.reconstructPath();
            }
            return newPos;
        },
        //get the window the movingEnt is currently over
        //we need to pass in the currently movind entity since
        //we want the distances between the center of that
        //entity and the center of the window
        getCurrentWindow: function (movingEnt) {
            
            if(!this.cacheValid) {
                this.windowsCache = this.getWindowsOnPath();
                this.cacheValid = true;
            }
            var windowsOnPath = this.windowsCache;
            //this loop goes through all the windows on the path
            //and finds the one that is closest to the currentPosition
            var minDistance = Infinity;
            var retval = null;
            //apperently doing this produces "small" performence
            //increases of 10-20 ms on large arrays (of which this is not one)
            //who the hell knows
            var len = windowsOnPath.length;
            for (var i = 0; i < len; ++i) {
                if (movingEnt.distanceTo(windowsOnPath[i]) < minDistance) {
                    retval = windowsOnPath[i];
                    minDistance = movingEnt.distanceTo(windowsOnPath[i]);
                }
            }
            return retval;
        },
        constructPath: function (startingLocation, path, speed) {
            var lastPos = startingLocation;
            this.movements = [];
            this.gridPath = [];
            for (var i = 0; i < path.length; ++i) {
                var newPosition = ig.game.level.getNewIndex(lastPos, path[i], { checkCollisions: false });

                //record the new floor,window index as we construct the path
                this.gridPath.push(newPosition);
                var newRealPosition = ig.game.level.getPosition(newPosition);
                var lastRealPosition = ig.game.level.getPosition(lastPos);
                var nextMovement = new Movement(lastRealPosition, newRealPosition, speed);
                lastPos = newPosition;
                this.movements.push(nextMovement);

            }
            this.endGrid = { floor: lastPos.floor, window: lastPos.window };
            this.currentMovement = 0;
        },
        reconstructPath: function () {
            var oldMovement = this.currentMovement;
            this.constructPath(this.start, this.path, this.speed);
            this.currentMovement = oldMovement;
        }

    });
});
