/**
 *  Copyright 2010 Mario Zechner(contact@badlogicgames.com) 
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License. 
 *  You may obtain a copy of the License at 
 *  http://www.apache.org/licenses/LICENSE-2.0 
 *  Unless required by applicable law or agreed to in writing, 
 *  software distributed under the License is distributed on an 
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 *  either express or implied. See the License for the specific 
 *  language governing permissions and limitations under the License. 
 */

/**
 * Couple of constants and variables governing 
 * the game settings. You can change those to 
 * your liking.
 */
const LEFT = 0;
const RIGHT = 1;
const UP = 2;
const DOWN = 3;
var SNAKE_SPEED = 0.1; // snake advances every SNAKE_SPEED seconds
var SNAKE_COLOR = "rgb(0,0,255)"; 
var NOM_COLOR = "rgb(255,0,0)";
var GRID_SIZE = 10; // size of a grid cell, snake and nom share this size.
var TROLL_PATH="../downloads/troll.png"; 

// instantiate the application which will update and draw
// the activities as well as deliver key pressed to them
var app = new Application( );
window.onload = function() { app.init() };

/**
 * Application class, contains the main loop which
 * does updating and rendering of the current activity.
 * An activity is a part of the game like the main menu,
 * the game itself or the game over screen. When an activity
 * ends it will set the next activity.
 */
function Application()
{
	/** the canvas **/
	this.canvas = null;
	/** the graphics context **/
	this.g = null;
	/** the current activity, e.g. main menu, game etc. **/
	this.activity = null;
	/** last frame time **/
	this.lastFrameTime = new Date().getTime();
	/** delta time in seconds **/
	this.delta = 0;
	
	/**
	 * Init functions, retrieves the canvas, creates the context
	 * and sets the timer event.
	 */
	this.init = function()
	{
		this.activity = new MainMenu();
		this.canvas = document.getElementById('com.badlogic.snake.Canvas');
		this.g = this.canvas.getContext('2d');
		app = this;
		
		document.onkeydown = function(event){
			app.keyDown(event);
		};
		
		setInterval( function(){
			app.main();
		}, 1000 / 30);
	}
	
	/**
	 * Main function, updates the current activity and lets 
	 * it draw itself.
	 */
	this.main = function()
	{
		currFrameTime = new Date().getTime();
		this.delta = (currFrameTime - this.lastFrameTime) / 1000;
		this.lastFrameTime = currFrameTime;
		 
		this.activity.update( this.delta );
		this.activity.draw();
	}
	
	/**
	 * Key event handler, delegates to current activity
	 */
	this.keyDown = function(event)
	{
		this.activity.keyDown(event);
	}
}

/**
 * The game activity, handles the snake, the nom and renders them.
 * Starts the GameOver activity when the snake died. 
 */
function Game( )
{	
	/** the snake **/
	this.snake = new Snake();
	/** time accumulator **/
	this.accum = 0;
	/** the nom **/
	this.nom = null;

	/**
	 * Updates the snake based on the given delta,
	 * checks for collisions, nom eating and acts
	 * accordingly
	 * @param {Object} delta
	 */
	this.update = function( delta )
	{
		this.accum += delta;
		
		while (this.accum > SNAKE_SPEED) 
		{
			this.accum-=SNAKE_SPEED;
			this.snake.update();
			if( this.checkCollision( ) )
				app.activity = new GameOver( (this.snake.parts.length - 9)*10 );
				
			if( this.snake.x == this.nom.x &&
				this.snake.y == this.nom.y )
			{
				this.snake.grow();
				this.placeNomNomNom();		
			}
		}
	}
	
	/**
	 * Checks whether the snake collided with itself
	 */
	this.checkCollision = function( )
	{
		var collided = false;
		for( var i = 0; i < this.snake.parts.length; i++ )
		{
			if( this.snake.parts[i].x === this.snake.x &&
				this.snake.parts[i].y === this.snake.y )
			{
				collided = true;
				break;
			}
		}
		return collided;
	}
	
	/**
	 * places a new nom, not really optimal and might take quiet some time
	 * when the snake fills most of the screen. i bet on people quitting the
	 * game before getting that far :p
	 */
	this.placeNomNomNom = function( )
	{
		while (true) 
		{
			this.nom = new NomNomNom( Math.random() *  300, Math.random() * 250 );
			if( !this.snake.collides( this.nom ) )
			break;
		}
	}
	
	/**
	 * Draws the snake, the nom and the score
	 */
	this.draw = function( )
	{
		app.g.fillStyle = "rgb(255,255,255)";
		app.g.fillRect( 0, 0, app.canvas.width, app.canvas.height );
		
		app.g.fillStyle = SNAKE_COLOR;
		app.g.fillRect( this.snake.x, this.snake.y, GRID_SIZE, GRID_SIZE );
		
		for( var i = 0; i < this.snake.parts.length; i++ )
			app.g.fillRect( this.snake.parts[i].x + 1, this.snake.parts[i].y + 1, GRID_SIZE - 2, GRID_SIZE - 2 );
		
		app.g.fillStyle = NOM_COLOR;
		app.g.fillRect( this.nom.x, this.nom.y, GRID_SIZE, GRID_SIZE );
		
		app.g.fillStyle = "rgb(0,0,0)";
		app.g.fillText( "score: " + (this.snake.parts.length - 9)* 10, 10, 10 );
	}
	
	/**
	 * Handles key events and lets the snake act accordingly
	 * @param {Object} event
	 */
	this.keyDown = function(event)
	{
		if (event.keyCode === 65 && this.snake.dir !== RIGHT ) {
			this.snake.nextDir = LEFT;
		}
		
		if( event.keyCode === 68 && this.snake.dir !== LEFT ) {
			this.snake.nextDir = RIGHT;
		}
		
		if( event.keyCode === 87 && this.snake.dir !== DOWN ) {
			this.snake.nextDir = UP;
		}
		
		if( event.keyCode === 83 && this.snake.dir !== UP ) {
			this.snake.nextDir = DOWN;
		}
	}

	// placing the nom!	
	this.placeNomNomNom();
}

/**
 * Snake class, holds the snake's head position, it's current direction
 * as well as his tail in form of an array. Might not be optimal but meh :p
 */
function Snake()
{	
	this.x = Math.floor(150) - (Math.floor(150) % GRID_SIZE);
	this.y = Math.floor(125) - (Math.floor(125) % GRID_SIZE);
	this.dir = RIGHT;
	this.nextDir = RIGHT;
	this.parts = new Array( );
	for( var i = 1; i < 10; i++ )
		this.parts.push( new SnakePart(this.x-GRID_SIZE*i, this.y) );
	this.growing = false;
	
	/**
	 * Updates the snake and its tail
	 */
	this.update = function()
	{	
		this.dir = this.nextDir;
		if (this.growing == 0) {
			for (var i = this.parts.length - 1; i >= 1; i--) {
				this.parts[i].x = this.parts[i - 1].x;
				this.parts[i].y = this.parts[i - 1].y;
			}
			
			this.parts[0].x = this.x;
			this.parts[0].y = this.y;
		}
		else
		{
			// yes there might be better ways :)
			newParts = new Array();
			newParts.push( new SnakePart( this.x, this.y ) );
			for( var i = 0; i < this.parts.length; i++ )
				newParts.push( this.parts[i] );
			
			this.parts = newParts;
			this.growing--;
		}
		
		if( this.dir === LEFT )
			this.x -= GRID_SIZE;
		if( this.dir === RIGHT )
			this.x += GRID_SIZE;
		if( this.dir === UP )
			this.y -= GRID_SIZE;
		if( this.dir === DOWN )
			this.y += GRID_SIZE;
		
		if( this.x >= 300 )
			this.x = 0;
		if( this.x < 0 )
			this.x = 300 - GRID_SIZE;
		if( this.y >= 250 )
			this.y = 0;
		if( this.y < 0 )
			this.y = 250 - GRID_SIZE;
	}
	
	/**
	 * Checks whether the given point collides with
	 * any part of the snake
	 * @param {Object} point
	 */
	this.collides = function( point )
	{
		if( this.x == point.x && this.y == point.y )
			return true;	
		for( var i = 0; i < this.parts.length; i++ )
			if( this.parts[i].x == point.x && this.parts[i].y == point.y )
				return true;
		return false;
	}
	
	/**
	 * Grows the snake by 4 parts
	 */
	this.grow = function( )
	{
		this.growing = 4;
	}
}

/**
 * A SnakePart is actually only a vector :)
 * @param {Object} x
 * @param {Object} y
 */
function SnakePart( x, y )
{
	this.x = x;
	this.y = y;
}

/**
 * And a nomnomnom is also only a vector
 * @param {Object} x
 * @param {Object} y
 */
function NomNomNom( x, y )
{
	this.x = Math.floor(x) - (Math.floor(x) % GRID_SIZE);
	this.y = Math.floor(y) - (Math.floor(y) % GRID_SIZE);
}

/**
 * Main menu activity, simply shows the menu text, let's
 * the "press (s) to continue" blink! Hurray for blinking
 * text!
 */
function MainMenu( )
{
	this.startTime = new Date().getTime();
	
	this.update = function( delta )
	{
		
	}
	
	this.draw = function( )
	{
		app.g.fillStyle = "rgb(255,255,255)";
		app.g.fillRect( 0, 0, app.canvas.width, app.canvas.height );
		
		var width = app.g.measureText( "-=[ NOM NOM NOM ]=-" ).width;
		var width2 = app.g.measureText( "Controls: w,a,s,d" ).width;
		var width3 = app.g.measureText( "Press 's' to continue" ).width;
		app.g.fillStyle = "rgb(255,0,0)";
		app.g.fillText( "-=[ NOM NOM NOM ]=-", 150 - width / 2, 125-10 );
		app.g.fillStyle = "rgb(0,0,255)";
		app.g.fillText( "Controls: w,a,s,d" , 150 - width2 / 2, 127 );
		
		var elapsed = Math.floor(( new Date().getTime() - this.startTime ) / 1000);
		if (elapsed % 2 == 0) 
		{
			app.g.fillStyle = "rgb(0,0,0)";
			app.g.fillText("Press 's' to continue", 150 - width3 / 2, 157);
		}
	}
	
	this.keyDown = function(event)
	{
		if( event.keyCode == 83 )
			app.activity = new Game();
	}
	
	this.keyUp = function(event)
	{
		
	}
}

/**
 * Game over activity, same as main menu, just shows the score
 * and prompts user to press s to continue
 * @param {Object} score
 */
function GameOver( score )
{
	this.score = score;
	this.startTime = new Date().getTime();
	this.troll = new Image();
	this.troll.src = TROLL_PATH;
	
	this.update = function( delta )
	{
		
	}
	
	this.draw = function( )
	{
		app.g.fillStyle = "rgb(255,255,255)";
		app.g.fillRect( 0, 0, app.canvas.width, app.canvas.height );
		
		app.g.drawImage( this.troll, app.canvas.width / 2 - 80 / 2, 20, 80, 80 );
		
		var width = app.g.measureText( "Game Over Buddy!" ).width;
		var width2 = app.g.measureText( "Score: " + this.score + "pts" ).width;
		var width3 = app.g.measureText( "Press 'x' to continue" ).width;
		app.g.fillStyle = "rgb(255,0,0)";
		app.g.fillText( "Game Over Buddy!", 150 - width / 2, 125-10 );
		app.g.fillStyle = "rgb(0,0,255)";
		app.g.fillText( "Score: " + this.score + "pts", 150 - width2 / 2, 127 );
		
		var elapsed = Math.floor(( new Date().getTime() - this.startTime ) / 1000);
		if (elapsed % 2 == 0) 
		{
			app.g.fillStyle = "rgb(0,0,0)";
			app.g.fillText("Press 'x' to continue", 150 - width3 / 2, 157);
		}
		
	}
	
	this.keyDown = function(event)
	{
		if( event.keyCode == 88 )
			app.activity = new Game( );
	}
}

