const FPS = 30;
const GRAVITY_INC = 0.9;
const GRAVITY_MAX = 15.0;
const JUMP_GRAVITY = -17.0;
const XSPEED_INC = 0.5;
const XSPEED_STOP_INC = 1;
const XSPEED_MAX = 9;
const XSPEED_MIN = -XSPEED_MAX;
var ctx, game;

var Sprite = new Class ({
    element: {},
    image: {},
    x: 0,
    y: 0,
    bounds: {},
    halfwidths: {},
    radius: 0, 
    collidable: true, 
    zindex: 0,
    src: '',
    
    initialize: function (args) {
        $extend(this, args);
        
        this.element = document.createElement('img');
        this.element.style.zIndex = this.zindex;
        ctx.appendChild(this.element);
        
        this.image = new Image();
        this.image.src = this.element.src = this.src;     
        
        var self = this;
        this.image.addEventListener('load', function(){
            self.halfwidths = {x: self.image.width / 2, y: self.image.height / 2};
            self.radius = self.halfwidths.x + self.halfwidths.y;
            game.assets_loaded++;
            self.draw();
        }, false);
    },
    
    collide: function(sprite, collision, quad){
        if($defined(this.before_collide)) this.before_collide(sprite, collision, quad);
        
        if(Math.abs(collision[0]) < Math.abs(collision[1])){
            if(sprite instanceof Player) sprite.xspeed = 0;
            sprite.x -= collision[0];
        } else {
            if(sprite instanceof Player){
                sprite.yspeed = 0;
                sprite.standing = true;
            }
            sprite.y -= collision[1];
        }
        
        if($defined(this.after_collide)) this.after_collide(sprite, collision, quad);
    },
    
    collision_check: function(sprite) {
        var vector = [this.middle[0] - sprite.middle[0], this.middle[1] - sprite.middle[1]];
        var distance = Math.abs(vector[0]) + Math.abs(vector[1]);
        
        if(this.radius + sprite.radius < Math.abs(vector[0]) + Math.abs(vector[1])){ 
            // They're not even close enough to collide
            return false;
        }
        
        var xside = vector[0] > 0 ? ['l', 'r'] : ['r', 'l'];
        var yside = vector[1] > 0 ? ['t', 'b'] : ['b', 't'];
        
        var xx = this.bounds[xside[0]] - sprite.bounds[xside[1]];
        var yy = this.bounds[yside[0]] - sprite.bounds[yside[1]];
        
        var xcollide = ((xside[0] == 'r' && xx >= 0 ) || (xside[0] == 'l' && xx <= 0));
        var ycollide = ((yside[0] == 't' && yy < 0) || (yside[0] == 'b' && yy >= 0));
        
        if(xcollide && ycollide){
            var collision = [xx, yy];
            sprite.collide(this, collision, [xcollide, ycollide]);
            return true;
        };
        return false;        
    },
    
    draw: function () {
        this.move_to(this.x, this.y);
        this.reset_bounds();
    },
    
    reset_bounds: function () {
        this.middle = [this.x + this.halfwidths.x, this.y + this.halfwidths.y];
        this.bounds = {
            't': this.y,
            'r': this.x + this.image.width,
            'b': this.y + this.image.height,
            'l': this.x
        };
    },
    
    move_to: function (x,y) {
        this.element.style.left = [x, 'px'].join('');
        this.element.style.top = [y, 'px'].join('');
    }
    
});

var Player = Sprite.extend({
    xaccel: 0,
    xspeed: 0,
    xmax: 0,
    xmin: 0,
    xstop: false,
    yspeed: 0,
    ymax: 0,
    standing: false,
    src: 'images/kitten.png',
    zindex: 100,
    
    initialize: function(src){
        this.parent({});
        
        var self = this;
        this.image.addEventListener('load', function() {
            self.xmax = 800 - self.image.width;
            self.ymax = 500 - self.image.height;
        }, false);
        
        window.addEventListener('keydown', function (e) {
            if(e.keyCode == 37) game.player.xaccel = -XSPEED_INC; game.player.xstop = false;
            if(e.keyCode == 38) game.player.jump();
            if(e.keyCode == 39) game.player.xaccel = XSPEED_INC; game.player.xstop = false;
            if(e.keyCode == 80) game.pause();
        }, false);
        
        window.addEventListener('keyup', function (e) {
            if(e.keyCode == 37 || e.keyCode == 39){
                game.player.xstop = true;
            }
        }, false);
        
    },
    
    tick: function () {
        
        if(this.jumping){
            this.y = this.y - 1;
            this.yspeed = JUMP_GRAVITY;
            this.jumping = false;
        } else {
            this.collide_all();
        }
        
        if(!this.standing && this.yspeed < GRAVITY_MAX){
            this.yspeed += GRAVITY_INC;
        }
        
        if(this.xstop){
            this.xaccel = 0;
            this.xspeed = Math.round(this.xspeed);
            if(this.xspeed == 0){
                this.xstop = false;
            } else if(this.xspeed > 0) {
                this.xspeed -= XSPEED_STOP_INC;
            } else {
                this.xspeed += XSPEED_STOP_INC;
            }
        }
        
        this.xspeed += this.xaccel;
        if(this.xspeed > XSPEED_MAX) this.xspeed = XSPEED_MAX;
        else if (this.xspeed < XSPEED_MIN) this.xspeed = XSPEED_MIN;
        
        this.y = Math.round(this.y + this.yspeed);
        this.x = Math.round(this.x + this.xspeed);
        
        if(this.y > this.ymax) this.death();
        
        this.draw();
    },
    
    death: function () { game.stop(); },
    jump: function () { this.jumping = (this.standing || this.y == this.ymax) },
    
    collide_all: function () {
        this.standing = false;
        for(i in game.actors){
            if(game.actors[i].collidable){
                this.collision_check(game.actors[i]);
            }
        }
    }

});

var Game = new Class({
    paused: false,
    actors: [],
    assets_loaded: 0,
    
    initialize: function (actors_array) {
        var self = this;
        for(i in actors_array){
            var e = actors_array[i];
            e[1].zindex = i;
            this.actors.push(new e[0](e[1]));
        }
        this.player = new Player();
        this.loader = setInterval(function() {
            if(self.assets_loaded >= self.actors.length) {
                clearInterval(self.loader);
                self.start();
            };
        }, 5);
    },

    stop: function () {
        clearInterval(this.interval);
        this.interval = false;
    },

    start: function () {
        var self = this;
        this.interval = setInterval(function() {self.player.tick();}, 1000/FPS);
    },
    
    pause: function () {
        (this.interval) ? this.stop() : this.start();
    }
    
});

var BadGuy = Sprite.extend({src: 'images/uberman.png'});
var Block = Sprite.extend({src: 'images/brick_block.gif'});

window.addEventListener("load", function () {
    var actors_array = [
        [BadGuy, {x: 100, y: 280}],
        [BadGuy, {x: 300, y: 240}]
    ];
    
    for(b = 0; b < 9; b++){
        actors_array.push([Block, {x: b * 80, y: 420}]);
    }
    
    ctx = document.getElementById('canvas');
    
    game = new Game(actors_array);
}, false);