ig.module('game.behavior')
.requires(
    'impact.entity',
    'impact.impact',
    'game.entities.brick',
    'game.path',
    'game.utils',
    'impact.timer',
    'impact.animation'
)
.defines(function () {
    "use strict";
    //behaviors are things that entities can do
    //they are functions and the behavior class is
    //just a set of them
    //
    //these functions are likely to change as
    //this programmer figures out the best ways
    //to represent time, events, and change
    ig.global.Behavior = {};
    ig.global.Behavior.Player = {};
    ig.global.Behavior.idle = function (time, source, target) {
        var opts = source.aiOptions;
        //source.currentAnim = source.movementAnims.idle;
        if (source.behaviorTimers.brick.delta() > opts.minTimeBetweenThrow &&
            (Math.random() < opts.throwBrickChance ||
            source.behaviorTimers.brick.delta() > opts.maxTimeBetweenThrow &&
            source.justAttacked === false)) {

            source.movementLock = false;
            source.behaviorTimers.brick.reset();
            source.justAttacked = true; //entity global varable that indicates ralph has just attacked
            source.behavior = Behavior.attack.bind(window, new ig.Timer(), source, target);

        } else if (source.behaviorTimers.yell.delta() > opts.minTimeBeforeYell &&
            (Math.random() < opts.randomYell ||
            source.behaviorTimers.yell.delta() > opts.maxTimeBeforeYell)) {
            source.movementLock = false;
            source.justAttacked = false;
            source.behaviorTimers.yell.reset();
            source.behavior = Behavior.yell.bind(window, new ig.Timer(), source, target);

        } else {
            if (source.movementLock === false) {
                source.justAttacked = false;
                source.behavior = Behavior.move.bind(window, new ig.Timer(), source, target);
                source.movementLock = true;

            }
        }
    };
    ig.global.Behavior.attack = function (time, source, target) {
        source.currentAnim = source.movementAnims.attack;
        var deltaTimer = time.delta();
        var spawnBrick = function () {
            source.currentAnim.gotoFrame(source.currentAnim.frame + 1);
            var pos = Utils.convertFromCornerToCenter(source, source.pos);
            pos.x -= source.offset.x;
            pos.y -= source.offset.y;
            var ent = ig.game.spawnEntity(EntityBrick, pos.x, pos.y);
            ent.pos = Utils.convertFromBottomCenterToCorner(ent, ent.pos);
        };
        if (source.behaviorTimers.brick.delta() > source.aiOptions.throwBrickDuration * 2.1) {
            source.behaviorTimers.brick.reset();
            source.behavior = Behavior.idle.bind(window, new ig.Timer(), source, target);
        }
        if (time.delta() > source.aiOptions.timeBetweenBricks) {

            spawnBrick();
            time.reset();
        }
    };
    ig.global.Behavior.move = function (time, source, target) {
        //gets a movement target that is not at the same
        //position as ralph
        var getTarget = function () {
            var target = Math.round((6 * Math.random()) % 5);
            if (target !== source.gridPosition.window) {
                return target;
            } else {
                return getTarget();
            }
        }
        var target = getTarget();
        var path = source.getPathTo({ floor: source.gridPosition.floor, window: target });

        var movementDirection = path.currentDirection();
        //set the correct animation
        if (movementDirection === Utils.directionsEnum.LEFT) {
            source.currentAnim = source.movementAnims.walkLeft;
        } else if (movementDirection === Utils.directionsEnum.RIGHT) {
            source.currentAnim = source.movementAnims.walkRight;
        }
        source.behavior = Behavior.followPath.bind(window, new ig.Timer(), source, path, Behavior.idle.bind(window, new ig.Timer(), source, target));

    };
    ig.global.Behavior.yell = function (timer, source, target) {
        var yellTime = 2;
        if (timer.delta() > yellTime) {
            source.behaviorTimers.yell.reset();
            source.behavior = Behavior.idle.bind(window, new ig.Timer(), source, target);
        } else {
            source.currentAnim = source.movementAnims.yell;
        }
    };
    ig.global.Behavior.levelTransition = function (time, source, target) {
        ig.game.player.lock = true;
        UI.levelChangeStart();
        source.currentAnim = source.movementAnims.climb;
        source.vel = { x: 0, y: -100 };
        if (source.pos.y < -source.size.y) {
            source.transitionLock = true;
            source.currentAnim = source.movementAnims.idle;


            //chain the delay and the jump behavior together
            source.behavior = Behavior.stand.bind(window, source,
                Behavior.delay.bind(window, new ig.Timer(), 2,
                    Behavior.jump.bind(window, new ig.Timer(), source)
                )
            );
        }
    };
    ig.global.Behavior.stand = function (ent, cont) {
        ent.pos = ig.game.level.getPosition(ent.gridPosition);
        ent.currentAnim = ent.movementAnims.idle;
        ent.behavior = cont;
    }
    //jandles the jump that breaks the window
    //note that we pass a lambda function as a sort of
    //continuation to the followpath function
    ig.global.Behavior.jump = function (timer, source, target) {
        ig.game.player.lock = true;
        source.currentAnim = source.movementAnims.jump;
        //copying the start and end points, there are not the same as the source pos
        //and they can be changed later on in the path getUpdatePosition function
        var startPoint = { x: source.pos.x, y: source.pos.y };
        var endPoint = { x: source.pos.x, y: source.pos.y };
        var path = new PathBase([startPoint, { x: source.pos.x, y: source.pos.y - IMG.SIZE(100) }, endPoint], 0.5);
        source.behavior = Behavior.followBasicPath.bind(window, new ig.Timer, source, path, function () {
            if (ig.game.level.pieSpawner) {
                ig.game.level.pieSpawner.enabled = true;
            }
            if (ig.game.level.duckSpawner) {
                ig.game.level.duckSpawner.enabled = true;
            }
            source.behavior = function (timer, source, target) {
                ig.game.soundEarthquake.play();
                //LANGFEATURE: this loop relies on iterators
                //for (var window in ig.game.level) {
                //    window.breakFully();
                //}

                //this loop does not need iterators
                ig.game.soundBreak.play();
                for (var i = 0; i < ig.game.level.floors.length - 2; ++i) {
                    for (var j = 0; j < ig.game.level.floors[i].windows.length; ++j) {
                        //and get the current window
                        ig.game.level.floors[i].windows[j].breakFully();
                        ig.game.level.floors[i].windows[j].hasInitialized = true;
                    }
                }
                ig.game.player.lock = false;
                UI.Timer.resetTimer(60);
                UI.levelChangeEnd();
                source.behavior = Behavior.idle.bind(window, new ig.Timer(), source, target);
            } .bind(window, new ig.Timer, source, target);
        });
    };
    ig.global.Behavior.remove = function (ent) {
        delete ent.behavior;
    };
    //target is the behavior to transition to, feel free to pass in lambdas
    //that is kinda the point
    ig.global.Behavior.followBasicPath = function (timer, source, path, cont) {
        var isDone = path.isDone();
        if (!isDone) {
            source.pos = path.getUpdatePosition(source);
        } else {
            cont();
        }
    };
    ig.global.Behavior.followPath = function (timer, source, path, target) {
        var isDone = path.isDone();

        if (!isDone) {

            //note that this will not update to the new, reconstructed path
            //but it wont matter since the next frame we will update to the new path
            source.pos = path.getUpdatePosition(source);

            //look to see if the current window is to be broken
            //if so go ahead and break it
            var currentWindow = path.getCurrentWindow(source);
            if(currentWindow.breakFully()) {
                ig.game.soundBreak.play();
            }

            
        } else {
            //make sure to update our level position when
            //we are done moving make sure to copy
            source.gridPosition = {
                floor: path.endGrid.floor,
                window: path.endGrid.window
            };
            source.currentAnim = source.movementAnims.idle;
            //when we are done call the continuation
            source.behavior = target;
        }
    };
    ig.global.Behavior.playerDie = function (time, source) {
        source.currentAnim = source.anims.fall;
        source.invincible = true;
        source.isDieing = true;
        source.vel = { x: IMG.SIZE(0), y: IMG.SIZE(600) };
        //| we are done falling, lets move the player
        if (source.pos.y >= ig.system.height) {
            source.pos = ig.game.level.getPosition(source.gridPosition);
            source.vel = { x: 0, y: 0 };
            source.currentAnim = source.anims.respawn;
            //| we are done falling, so start the respawn process
            source.isDieing = false;
            source.behavior = Behavior.and.bind(window, Behavior.playerRespawn.bind(window, new ig.Timer(), source), Behavior.Player.move.bind(window, new ig.Timer(), source));
        }
    };
    //executes behaviors a and be at the same time
    //note that as of this comment the outputs of the
    //behaviors are NOT piped
    ig.global.Behavior.and = function (a, b) {
        if (a) {
            a();
        }
        if (b) {
            b();
        }
    };
    //delays a behavior by a certain amount of time
    ig.global.Behavior.delay = function (time, amount, cont) {
        if (time.delta() >= amount) {
            cont();
        }
    };
    //handles the player respawning and being invincible for a certain amount of timer
    ig.global.Behavior.playerRespawn = function (time, player) {
        var respawnTime = 5;

        //check to see if the current animation is a respawn animation
        //by looking to see if it contains the 4th (blank) frame
        //if it is not construct a new respawn animation out of the current animation
        //using the same animSheet and concatinateing 4 onto the end of the sequence
        var respawnAnim = player.currentAnim;
        if (player.currentAnim.sequence.indexOf(7) === -1) {
            //use a temp varable to store the flip state of the animation so
            //it is preserved when we make out modified animation
            var flipval = respawnAnim.flip;
            respawnAnim = new ig.Animation(player.animSheet, respawnAnim.frameTime, respawnAnim.sequence.concat([7]), player.currentAnim.stop);
            player.currentAnim = respawnAnim;
            player.currentAnim.onAnimDone = function () {
                delete player.currentAnim.onAnimDone;
                player.currentAnim = player.anims.respawn;
                player.currentAnim.flip = flipval;

            }

            respawnAnim.flip = flipval;

        }
        if (time.delta() <= respawnTime) {
            player.currentAnim = respawnAnim;
            player.invincible = true;
        } else {
            player.invincible = false;
            player.currentAnim = player.anims.idle;

            delete player.behavior;
        }
    };
    ig.global.Behavior.Player.gameOver = function (timer, player, cont) {
        //player.currentAnim = player.anims.die;
        player.isDieing = true;
        if (player.currentAnim.frame >= 5) {
            player.currentAnim.angle = 0;
            cont();
        } else {
            if (timer.delta() > player.currentAnim.frameTime) {
                player.currentAnim.gotoFrame(player.currentAnim.frame + 1);
                timer.reset();
            } else {
                player.currentAnim.gotoFrame(player.currentAnim.frame);
            }
            //we want to rotate the player as he dies
            player.currentAnim.angle = player.currentAnim.frame * (Math.PI / 2);
        }


    };
    //handles getting movement and input
    ig.global.Behavior.Player.move = function (time, player) {
        //we want to lock out movement
        // if we are already moving
        if (!(player.movement)) {
            if (ig.input.pressed('right')) {
                player.handleMove("right");
            } else if (ig.input.pressed('left')) {
                player.handleMove("left");
            } else if (ig.input.pressed('up')) {
                player.handleMove("up");
            } else if (ig.input.pressed('down')) {
                player.handleMove("down")
            } else if (ig.input.pressed('fix')) {
                player.handleFix();
            }
        }
    };
    ig.global.Behavior.Player.eatPie = function (time, player) {
        var invincibleTime = 8;
        player.scoreMultiple = 2;
        var invincibleAnim = player.currentAnim;
        var invincibleFrames = player.currentAnim.sequence.filter(function (val) {
            return val > 4;
        });
        var isInvincible = undefined;
        if (invincibleFrames.length > 0) {
            isInvincible = true;
        } else {
            isInvincible = false;
        }
        if (player.invincibleTimeout) {
            window.clearTimeout(player.invincibleTimeout);
        }
        if (!isInvincible) {

            //create a new array with the shifted indices
            var newSequence = player.currentAnim.sequence.reduce(function (prv, val) {
                var nxt = [];
                nxt = nxt.concat(val + 4);
                nxt = nxt.concat(val + 4 * 2);
                nxt = nxt.concat(val + 4 * 3);
                return prv.concat(nxt);

            }, [])
            var flipTmp = invincibleAnim.flip; //make sure we keep the flip state
            //calculate a new frame time so that animation take the same amount of time as they did before
            var newTime = (player.currentAnim.frameTime * player.currentAnim.sequence.length) / newSequence.length;
            var invincibleAnim = new ig.Animation(player.animSheet, newTime, newSequence, player.currentAnim.stop);
            invincibleAnim.onAnimDone = function () {
                player.currentAnim = player.anims.idle;
                delete invincibleAnim.onAnimDone;
            }
            invincibleAnim.flip = flipTmp;

        }
        if (time.delta() <= invincibleTime) {
            player.currentAnim = invincibleAnim;
            player.invincible = true;
        } else {
            player.scoreMultiple = 1;
            player.invincible = false;
            player.currentAnim = player.anims.idle;
            delete player.behavior;
        }
    };


});
