/*
 *  PONG
 *
 *  A remake of PONG in a favicon, using JavaScript, Canvas and data:URIs.
 *
 *  (c) January 2009
 *  Mathieu 'p01' Henri for Jung von Matt.
 *  http://www.p01.org - httm://www.jvm.de
 */

var faviconGame = new (function()
{
    Function.prototype.bind = function( what )
    {
    	var	_method = this;
    	return function(){ return _method.apply( what, arguments ) };
    }

	var h               = {},
	    ctx				= null,
	    favicon         = null,
		data            = {},
        game            =
        {
            state       :'',
    		stateBefore	:'',
    		update		:{},
    		now			:0,
    		then		:0,
    		warmup      :1500
        },
		keys            =
		{
		    up          :{},
		    down        :{},
		    mapping     :
            {
    			38      :'up',
    			87      :'up',
    			40      :'down',
    			83      :'down',
    			37      :'left',
    			65      :'left',
    			39      :'right',
    			68      :'right',
    			68      :'right',
    			78      :'fire',
    			13      :'select',

    			0       :'** dummy **'
    		}
		};

	/*
	 *	initialize
	 */
	window.addEventListener
	(
		'load',
		(function(/*onload*/)
		{
            //  create canvas
		    var canvas  = document.createElement('canvas');
		    canvas.width = canvas.height = 16;
			//	does this browser support canvas ?
			if( 'function'!=typeof(canvas.getContext) )
			{
			    return false;
			}
			//	get rendering context
			ctx	= canvas.getContext('2d');
			//	does this browser support canvas.toDataURL ?
			if( 'function'!=typeof(ctx.canvas.toDataURL) )
			{
			    return false;
			}

            //  get the handle of the favicon
            h.favicon   = document.getElementById('favicon');
            if( !h.favicon )
            {
                h.favicon   = document.createElement('link');
                h.favicon.setAttribute( 'id',    'favicon' );
                h.favicon.setAttribute( 'rel',   'shortcut icon' );
                h.favicon.setAttribute( 'type',  'image/png' );
                var head    = document.getElementsByTagName('head')[0];
                head.appendChild( h.favicon );
            }

			// keyboard
			for( var i in keys.mapping )
			{
				keys.down[ keys.mapping[i] ] = keys.up[ keys.mapping[i] ] = 0;
			}
			onkeyup = onkeydown = handleKeyEvents.bind(this);

			//	blur/focus = pause/resume
			onblur	= onfocus	= (function(event){ var event = event||window.event; if(game.state=='play'||game.state=='pause')setState( event.type.toLowerCase()=='blur'?'pause':game.stateBefore); }).bind(this);

			//	start with the 'title' state
			setState( 'title' );

		    //  get the handle of the sprites
		    h.sprites = document.createElement('img');
		    h.sprites.onload    = function(){ this.isReady = true; };
            h.sprites.src = 'faviconGame.pong.sprites.png';

			// there we go
			main();

			return true;
		}).bind(this),
		false
	);


	/*
	 *	handleKeyEvents
	 */
	function handleKeyEvents( event )
	{
		var event	= event||window.event,
			type	= event.type.toLowerCase(),
			keyCode	= keys.mapping[event.keyCode]||'unknown';

		keys.down[keyCode]=type=='keydown'?1:0;
		keys.up[keyCode]=type=='keyup'?1:0;

		if( keyCode!='unknown' && event.preventDefault )
		{
		    event.preventDefault();
		}
	}


	/*
	 *	setState
	 */
	function setState( newState )
	{
		if( 'function'!==typeof(game.update[newState]) )
		{
		    throw new Error('state "'+ newState +'" not supported' );
		}

		game.then 		    = new Date().getTime();
		game.stateBefore	= game.state;
		game.state		    = newState;
	}


	/*
	 *	main
	 */
	function main()
	{
	    if( h.sprites.isReady )
	    {
    		game.now = new Date().getTime()-game.then;

    		//	clear
    		ctx.globalCompositeOperation = 'source-over';
    		ctx.globalAlpha = 1;
    		ctx.fillStyle   = '#000';
    		ctx.fillRect( 0,0,16,16 );

    		// actual update
    	    game.update[game.state]();

    		// set favicon
    		var newIcon = h.favicon.cloneNode(true);
    		newIcon.setAttribute('href',ctx.canvas.toDataURL());
    		h.favicon.parentNode.replaceChild(newIcon,h.favicon);
    		h.favicon=newIcon;

    		//	reset keyUp
    		for( var i in keys.mapping )
    		{
    			keys.up[keys.mapping[i]]=0;
    		}
		}

		//	update
		setTimeout( arguments.callee.bind(this), 50 );
	}


	/*
	 *	initializeGame
	 */
	function initializeGame()
	{
		//	reset data
		data.py     = 4;
        data.turn   = Math.random()>.5;
        data.cy     = 4;
        data.ps     = 0;
        data.cs     = 0;
        data.cyd    = (Math.random()*20&31)/2;
        resetBall();

		//	play!
		setState('play');
	}
	/*
	 *  resetBall
	 */
	function resetBall()
	{
        data.bx     = data.turn?2:13;
        data.by     = (1+Math.random()*14)&15;
        data.bxi    = data.turn?1:-1;
        data.byi    = Math.random()*2-1;

		ctx.globalCompositeOperation    = 'lighter';
		ctx.fillStyle   = '#360';
		ctx.fillRect( 0,0,16,16);
		ctx.globalCompositeOperation    = 'source-over';

	}


	/*
	 *	game.update.title
	 */
	game.update.title = function()
	{
		var n = 4*game.now/3373;

		if( n<2 )
		{
    		ctx.drawImage( h.sprites, Math.round( (n-2)*16 ),-48 );
    		//ctx.drawImage( h.sprites, Math.round( (2-n)*16 ),48,16,16,0,0,16,16 );
		}
		else if( n<3 )
		{
    		ctx.drawImage( h.sprites, 0,Math.round( (n-5)*16 ) );
		}
		else if( n<4 )
	    {
    		ctx.drawImage( h.sprites, Math.round( (3-n)*16 ),-32 );
	    }
	    else
        {
    		ctx.globalAlpha = (7+Math.cos( n*6.2832/2 ))/8;
    		ctx.drawImage( h.sprites, -16,-32 );
    		ctx.globalAlpha = 1;
        }

		if( keys.up.fire && n>4 )
		{
			initializeGame();
		}
	}


	/*
	 *	game.update.gameover
	 */
	game.update.gameover = function()
	{
		if( game.now<game.warmup*2 )
		{
		    ctx.drawImage( h.sprites, -16-Math.round( 16*game.now/game.warmup )*(data.ps==10?-1:1), 0 );
		}
		else
	    {
			setState('title');
		}
	}


	/*
	 *	game.update.pause
	 */
	game.update.pause = function()
	{
		ctx.drawImage( h.sprites, -16, Math.round( -16*Math.min(1,game.now/game.warmup) ) );
		ctx.globalCompositeOperation    = 'lighter';
		ctx.fillStyle   = '#360';
		ctx.globalAlpha = .5-.5*Math.cos( (game.now/337)%6.283 );
		ctx.fillRect( 0,0,16,16);
		ctx.globalAlpha = 1;
		ctx.globalCompositeOperation    = 'source-over';

	}


	/*
	 *	game.update.play
	 */
	game.update.play = function()
	{
		var isStarting	= game.now<game.warmup;

		//	start transition
		if( isStarting )
		{
			ctx.drawImage( h.sprites, -16, Math.round( -(game.stateBefore=='pause'?16:32)*(1-game.now/game.warmup) ) );
		}
		//	playing
		else
		{
			ctx.drawImage( h.sprites, -16,0 );

            ctx.drawImage( h.sprites, data.ps*4,65,3,5, 2,1,3,5 );
            ctx.drawImage( h.sprites, data.cs*4,65,3,5, 10,1,3,5 );

            ctx.fillStyle = '#fff';
            ctx.fillRect(  0, data.py, 1,6 );
            ctx.fillRect( 15, data.cy, 1,6 );
            ctx.fillRect( data.bx, data.by, 1,1 );

            //  player logic
            if( data.py>0 && keys.down.up )
            {
                data.py -=.5;
            }
            if( data.py<10 && keys.down.down )
            {
                data.py +=.5;
            }

            //  cpu logic
            if( data.cy==data.cyd )
            {
                data.cyd    = (Math.random()*20&31)/2;
            }
            data.cy += data.cy>data.cyd?-.5:.5;

            //  ball logic
            data.by += data.byi;
            if( data.by<0 || data.by>15 )
            {
                data.by += data.byi = (Math.random()+1)/(data.byi<0?2:-2);
            }

            data.bx += data.bxi;
            if
            (
                ( data.bx<1  && Math.abs( data.py+3-data.by )<3 ) ||
                ( data.bx>14 && Math.abs( data.cy+3-data.by )<3 )
            )
            {
                data.bx += data.bxi = -data.bxi;
            }
            else if( data.bx<0 )
            {
                data.cs++;
                data.turn = true;
                resetBall();
            }
            else if( data.bx>15 )
            {
                data.ps++;
                data.turn = false;
                resetBall();
            }
            if( data.ps==10 || data.cs==10 )
            {
                setState('gameover');
            }
		}
	}


})();

