
function debug(id, msg) {
    var debug = document.getElementById(id + '_debug');
    if (!debug) {
        debug = document.createElement('div');
        debug.id = id + '_debug';
        var body = document.getElementsByTagName('body')[0];
        body.appendChild(debug);
    };
    while (debug.hasChildNodes()) {
        debug.removeChild(debug.lastChild);
    };
    debug.appendChild(document.createTextNode(msg));
};

var global = this;
global.jsgame = new function() {
    /* very simple gaming library for creating (arcade-style) games in JS */

    var jsgame = this;
    
    function state(doc) {
        /* maintain the state of the keys and mouse */
        if (doc) {
            this.doc = doc; // XXX should be 'root element' i guess...

            this.mouseclicks0 = 0;
            this.mouseclicks1 = 0;
            this.mouseclicks2 = 0;

            // register event handlers
            var self = this;
            doc.onkeypress = function(evt) {self.onkeyevent(evt)};
            doc.onkeyup = function(evt) {self.onkeyevent(evt)};
            doc.onmousedown = function(evt) {self.onmouseevent(evt)};
            doc.onmouseup = function(evt) {self.onmouseevent(evt)};
            doc.onmousemove = function(evt) {self.onmouseevent(evt)};
        };
    };

    this.state = state;

    var _key2name = {
        32: 'space',
        37: 'left',
        38: 'up',
        39: 'right',
        40: 'down',
    };
    this._key2name = _key2name;
    var _i = 0;
    state.prototype.onkeyevent = function(evt) {
        var type = evt.type;
        var code = evt.keyCode;
        if (code == 0) {
            code = evt.charCode;
        };
        var newvalue = (type != 'keyup') ? true : false;
        if (type == 'keypress' || type == 'keyup') {
            var keyname = _key2name[code];
            if (keyname) {
                this[keyname + 'pressed'] = newvalue;
            };
        };
        if (evt.stopPropagation) {
            evt.stopPropagation();
            evt.preventDefault();
        } else {
            evt.returnValue = false;
        };
        evt.cancelBubble = true;
    };

    state.prototype.onmouseevent = function(evt) {
        var type = evt.type;
        switch (type) {
            case 'mousemove':
                this.mousex = evt.clientX;
                this.mousey = evt.clientY;
                break;
            case 'mousedown':
                var btn = evt.button;
                this['mouse' + btn + 'down'] = true;
                this['mouseclicks' + btn] += 1
                break;
            case 'mouseup':
                this['mouse' + evt.button + 'down'] = false;
                break;
        };
    };

    function engine(doc, game) {
        /* the gaming 'engine', provides mainloop and such
        
            instantiate this with a reference to the document where the
            game should be instantiated (XXX 'game' not in use, should we
            do anything there like keep config?), then add some entities
            using 'add_entity', call 'create_canvas' to place a canvas on
            the screen, and lastly call 'start' to start the main loop
        */
        if (doc, game) {
            this.doc = doc;
            this.game = game;
            this.state = new state(doc);
            this.entities = [];
        };
    };

    this.engine = engine;

    engine.prototype.create_canvas = function() {
        /* create a canvas */
        var body = this.doc.getElementsByTagName('body')[0];
        var canvas = this.canvas = this.doc.createElement('div');
        canvas.appendChild(this.doc.createTextNode('\xa0'));
        canvas.style.width = this.game.width + 'px';
        canvas.style.height = this.game.height + 'px';
        canvas.style.position = 'absolute';
        canvas.style.left = this.game.left + 'px';
        canvas.style.top = this.game.top + 'px';
        canvas.style.border = '1px solid black';
        body.appendChild(canvas);
    };

    engine.prototype.start = function() {
        /* start the mainloop */
        var now = new Date();
        this.mainloop(now);
    };

    engine.prototype.mainloop = function(previous) {
        var now = new Date();
        var diff = (now - previous) / 1000;
        for (var i=0; i < this.entities.length; i++) {
            var entity = this.entities[i];
            entity.update(this.state, diff);
            for (var j=i+1; j < this.entities.length; j++) {
                var collentity = this.entities[j];
                if (this.check_collide(entity, collentity)) {
                    entity.collide(collentity);
                    collentity.collide(entity);
                };
            };
        };
        debug(0, 'fps: ' + Math.round(1 / diff));
        var self = this;
        misclib.schedule(this, this.mainloop, 0, now);
    };

    engine.prototype.check_collide = function(s1, s2) {
        var s1x1 = s1.left;
        var s1x2 = s1.left + s1.width;
        var s1y1 = s1.top;
        var s1y2 = s1.top + s1.height;
        var s2x1 = s2.left;
        var s2x2 = s2.left + s2.width;
        var s2y1 = s2.top;
        var s2y2 = s2.top + s2.height;
        if (s2x1 < s1x2 && s2x2 > s1x1 && s2y1 < s1y2 && s2y2 > s1y1) {
            return true;
        };
        return false;
    };

    engine.prototype.add_entity = function(entity) {
        this.entities.push(entity);
    };

    engine.prototype.del_entity = function(entity) {
        var newentities = [];
        for (var i=0; i < this.entities.length; i++) {
            if (entity !== this.entities[i]) {
                newentities.push(this.entities[i]);
            };
        };
        this.entities = newentities;
    };

    function entity(doc, game, left, top, width, height, speed) {
        /* base class for entities */
        if (doc) {
            this.doc = doc;
            this.game = game;
            this.left = left;
            this.top = top;
            this.width = width;
            this.height = height;
            this.speed = speed;
            this.name = 'unknown';
            this.init_entity();
        };
    };

    this.entity = entity;

    entity.prototype.init_entity = function() {
        var body = this.doc.getElementsByTagName('body')[0];
        var entity = this.entity = this.doc.createElement('div');
        entity.appendChild(this.doc.createTextNode('\xa0'));
        entity.style.position = 'absolute';
        entity.style.left = this.left + 'px';
        entity.style.top = this.top + 'px';
        entity.style.width = this.width + 'px';
        entity.style.height = this.height + 'px';
        entity.style.lineHeight = this.height + 'px';
        entity.style.backgroundColor = 'red';
        body.appendChild(entity);
    };

    entity.prototype.update = function(state, timespent) {
        /* some dummy implementation, moves the entity using the cursor keys */
        if (state.mouse0down) {
            this.entity.style.backgroundColor = 'black';
        } else {
            this.entity.style.backgroundColor = 'red';
        };
        var speed = this.speed || 200;
        if ((state.leftpressed || state.rightpressed) &&
                (state.uppressed || state.downpressed)) {
            speed = speed / 2;
        };
        if (state.leftpressed && this.left > 0) {
            this.left -= speed * timespent;
            if (this.left < 1) {
                this.left = 1;
            };
        };
        var maxleft = this.game.width - this.game.left - this.width;
        if (state.rightpressed && this.left < maxleft) {
            this.left += speed * timespent;
            if (this.left > maxleft) {
                this.left = maxleft;
            };
        };
        if (state.uppressed && this.top > this.game.left) {
            this.top -= speed * timespent;
            if (this.top < 1) {
                this.top = 1;
            };
        };
        var maxtop = this.game.height - this.game.top - this.height;
        if (state.downpressed && this.top < maxtop) {
            this.top += speed * timespent;
            if (this.top > maxtop) {
                this.top = maxtop;
            };
        };
        var framerate = 1 / timespent;
        this.entity.style.left = Math.round(this.left) + 'px';
        this.entity.style.top = Math.round(this.top) + 'px';
    };

    entity.prototype.collide = function(other) {
        debug('collide', 'collision of ' + this.name + ' with ' + other.name);
    };
}();


