/*	Original:  Kevin Peno (kevin@metalaxe.com)
	Web Site:  http://metalaxe.com

	Version: 1.0 alpha
	Date: 10/30/2007 @ 21:28 PST (-8 GMT)
	Requirements: jQuery, jQuery.ui
*/

function Battleship()
{
	/*
		Dragged ship is rotated
	*/
	this.dragged = '';
	
	/*
	*	Game ID
	*/
	this.gameId = null;

	/*
	*	Game State (nothing/setup/battle)
	*/
	this.gameState = 'pending';

	/*
	*	host?
	*/
	this.isHost = false;

	/*
		Information used to describe ships
	*/
	this.shiptypes = {
		cv : [ 'Aircraft Carrier',	5,	'carrier',		false ],
		bb : [ 'Battleship',		4,	'battleship',	false ],
		ss : [ 'Submarine',			3,	'sub',			false ],
		dd : [ 'Destroyer',			3,	'destroyer',	false ],
		pt : [ 'Patrol Boat',		2,	'patrol',		false ]
	};
	
	/*
		Information used to describe ships
	*/
	this.numToAlpha = [ 'A','B','C','D','E','F','G','H','I','J' ];
	
	/**
		Example player object
	
		this.player = {
						ships:
						{
							shipcall (ie. CV) :
							[
								X pos	(ie. 1),
								Y pos	(ie. A [numeric of] ),
								lives,
								direction	(true/false) (aka horizontal/vertical)
							]
						},
						grid:
						[
						 	[0,-1,5,5,5,5,0,0,0,0], (-1 is hit/invalid, 5 is ship id, this case cv )
							etc
						]
					};
	**/

		
	/*
		Player Elements
	*/
	this.player = {
		id: 0,
		ready: false,
		ships: 0,
		grid: [
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0]
		]
	};

	/*
		Opponent Elements
	*/
	this.enemy = {
		id: 0,
		ready: false,
		ships: 5,
		grid: [
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0]
		]
	};
}

/**
*	Init...
**/
Battleship.prototype.Init = function( gameId, userId, host )
{
	// For call backs
	var self = this;

	this.gameId = gameId;
	this.player.id = userId;
	this.isHost = host;

	/**
	*	Meteor connection init
	**/
	// Set this to something unique to this client
	Meteor.hostid = this.player.id;
	Meteor.debugmode = true;
	// Our Meteor server is on the data. subdomain
	Meteor.host = 'data.' + location.hostname;
	Meteor.mode = 'stream';
	// Join game Channels
	Meteor.joinChannel( gameId, 0);
	// Start streaming!
	Meteor.connect();

	if( !host )
	{
		// Call host and establish connection
		$.post('battleship.php', {
			game:	this.gameId,
			user:	this.player.id,
			action:	'program.challengeRequest(\'' + this.player.id + '\');'
		});
		$('#waiting').show();
	}
	else
	{
		$('#invitation').show();
		this.setup();
	}
}

/**
*	Check if challenger can connect
**/
Battleship.prototype.challengeRequest = function( user )
{
	if( user != this.player.id && !this.enemy.id )
	{
		if( confirm('Challenger requesting to join') )
		{
			// Send connected response
			$.post('battleship.php', {
				game:	this.gameId,
				user:	this.player.id,
				action:	'program.challengeAccepted( \'' + this.player.id + '\', true );'
			});
			this.enemy.id = user;
		}
		else
		{
			// Send failed response
			$.post('battleship.php', {
				game:	this.gameId,
				user:	this.player.id,
				action:	'program.challengeAccepted( \'' + this.player.id + '\', false );'
			});
		}
	}
}

/**
*	Incoming to notify client of accept/deny status of challenge request
**/
Battleship.prototype.challengeAccepted = function( user, accepted )
{
	alert( 'Challenge ' + ( accepted ? 'Accepted' : 'Denied' ) );
	if( accepted && this.gameState == 'pending' )
	{
		$('#waiting').hide();
		this.setup();
		this.enemy.id = user;
	}
	else if( this.isHost )
		$('#invitation').hide();
}

/*
Start the game!
*/
Battleship.prototype.setup = function()
{
	// For call backs
	var self = this;

	// Change game state to setup
	this.gameState = 'setup';

	$("#ships .horizontal img").draggable({
		cursor: 'move',
		cursorAt: {bottom:13, left:13},
		//revert: true,
		zindex: 1000,
		appendTo: '#player .grid .valid',
		start: function()
		{
			$(document).keypress( function(e){ self.rotateImage(e); });
			self.dragged = this;
			if( self.player.ships == 5 )
				$('#start').hide();

		},
		stop: function()
		{
			$(document).unbind('keypress');
			self.dragged = null;
		}
	});

	$('#shipselect').show();
	$('#img #ships .horizontal img').show();

	$('#player .grid .valid').droppable({
		accept : '#img #ships img',
		drop : function(e,ui){ return self.placePlayerShip(e,ui); }
	});
	$('#player').show();

	$('#start').click( function(e){ self.start(e); } );
}
/*
*	Start the actual fighting
*/
Battleship.prototype.start = function( e )
{
	if( this.player.ships < 5 )
	{
		alert( 'You must place all of your ships before the game can begin' );
		return false;
	}

	$('#start').hide();

	// Remove the dragg/droppability
	$('#ships img').draggable('disable').unbind('dblclick');
	$('#player div').droppable('disable');

	var self = this;
	$('#enemy').show();

	// Remove ship select screen
	$('#shipselect h3').hide();
	$('#ships img').css('cursor','default');

	//Send Ready response
	var playerGridQuery = '';
	$.post('battleship.php',
		{
			action:		'program.ready(\'' + this.player.id + '\')',
			game:		this.gameId,
			user:		this.player.id
		}
	);
	this.ready( this.player.id );
}

/**
*	Opponent sends ready signal
**/
Battleship.prototype.ready = function( user )
{
	if( user == this.player.id )
		this.player.ready = true;
	else if( user == this.enemy.id )
		this.enemy.ready = true;

	if( this.enemy.ready && this.player.ready )
	{
		if( this.isHost )
			this.startTurn();
		this.gameState = 'battle';
	}
}

/**
*	My Turn Start
**/
Battleship.prototype.startTurn = function()
{
	var self = this;
	$('#waiting-enemy').hide();
	$('#enemy div.valid').bind("click", function(e){ self.getTarget( this, e ) } );
}

/**
*	Fire a shot at the enemy!
**/
Battleship.prototype.fireShot = function( user, points )
{
	if( user == this.enemy.id && this.enemy.grid[ points.y ][ points.x ] === 0 )
	{
		$.post('battleship.php',
			{
				game:		this.gameId,
				user:		this.player.id,
				action:		'program.fireShot(\'' + this.enemy.id + '\',{ x:' + points.x + ', y:' + points.y +'})'
			}
		);
		this.enemy.grid[ points.y ][ points.x ] = -1;
		this.endTurn();
	}
	else if( user == this.player.id && this.player.grid[ points.y ][ points.x ] != -1 )
	{
		var hit = isNaN( this.player.grid[ points.y ][ points.x ] );
		$.post('battleship.php',
			{
				game:		this.gameId,
				user:		this.player.id,
				action:		'program.setImage(' + points.y + ',' + points.x + ',' + hit + ',\'' + this.player.grid[ points.y ][ points.x ] + '\',\'' + this.player.id + '\')'
			}
		);
	}
}

/**
*	My Turn End
**/
Battleship.prototype.endTurn = function()
{
	var self = this;
	$('#waiting-enemy').show();
	$('#enemy div.valid').unbind('click');
}

/**
*	Place Ship on grid during setup
**/
Battleship.prototype.placePlayerShip = function( e, jquery_ui )
{
	// Find our stuffs
	var target = this.findCell( e, jquery_ui );
	var ship = jquery_ui.draggable;
	var ship_key = ship.attr('class').substring(0,2);

	// If we haven't picked a ship this does nothing
	if( !ship )
	{
		return false;
	}

	// Did we move this ship before?
	if( typeof( this.player.ships[ ship_key ] ) == 'object' && this.player.ships[ ship_key ] && typeof( this.player.ships[ ship_key ][0] ) != 'undefined' )
	{
		// Clear the previous entries in the grid
		var ox = this.player.ships[ ship_key ][0];
		var oy = this.player.ships[ ship_key ][1];

		for( var oi = 0; oi < this.shiptypes[ ship_key ][1]; oi++ )
		{
			this.player.grid[ oy ][ ox ] = 0;
			if( this.player.ships[ ship_key ][3] )
				oy++;
			else
				ox++;
		}
		// Remove from the tally
		if( this.player.ships == 5 )
			$('#start').hide();

		this.player.ships--;
		this.player.ships[ ship_key ] = null;
	}

	// Check for enough room
	var conflict = false;
	var revertToGrid = this.player.grid;

	// Check we don't overlap other ships or hit the end of the grid
	var x = target.x;
	var y = target.y;
	var i = 0;

	// Apply this ship's location while we're here
	do{
		if( x < 0 || y < 0 )
			conflict = true;
		else if( this.player.grid[ y ][ x ] != 0)
			conflict = true;
		else
		{
			this.player.grid[ y ][ x ] = ship_key;
			i++;
			if( this.shiptypes[ ship_key ][3] )
				y++;
			else
				x++;
		}
	}while( !conflict && i < this.shiptypes[ ship_key ][1] && y < 11 && x < 11 );

	// See if we hit the end of the line
	if( ( y == 11 || x == 11 ) && i < this.shiptypes[ ship_key ][1] )
		conflict = true

	if( conflict )
	{
		alert( 'Please place the ' + this.shiptypes[ ship_key ][0] + ' to where all sides are within the ocean grid and not touching squares containing any other ships' );
		this.player.grid = revertToGrid;
		return false;
	}
	
	// Move the ship image to the grid where selected
	ship.css({
		top: target.offset.top,
		left : target.offset.left
	});
	
	//Update the player node
	this.player.ships[ ship_key ] = [ target.x, target.y, this.shiptypes[ ship_key ][1], this.shiptypes[ ship_key ][3] ];
	this.player.ships++;

	// Show start button
	if( this.player.ships === 5 )
		$('#start').show();

	// Remove draggability
	var self = this;
	ship.draggable('disable')
		.dblclick( function()
		{
			$(this).draggable('enable');
			$(this).unbind('dblclick');
		}
	);

	return true;
}

/**
*	Find the true targeted cell
*	Used during ship placement
**/
Battleship.prototype.findCell = function( e, jquery_ui )
{
	// Returned object
	var target = { x:null, y:null, offset:null };

	//Ship
	var draggable = jquery_ui.draggable;

	//Grid sizes
	var gridx = 30;
	var gridy = 30;
	
	//Grid div calcs
	var droppable = jquery_ui.element;
	var droppable_inset = droppable.offset();

	// Ship Image calcs
	var draggable_offset = draggable.offset();
	var draggable_inset_x = ( draggable_offset.left - droppable_inset.left );
	var draggable_inset_y = ( draggable_offset.top - droppable_inset.top );

	// "Grid" location calulations - gives a soft leniency of 5px overhanging
	target.x = ( draggable_inset_x % gridx ) < 25 ? Math.floor( draggable_inset_x / gridx ) : Math.ceil( draggable_inset_x / gridx );
	target.y = ( draggable_inset_y % gridy ) < 25 ? Math.floor( draggable_inset_y / gridy ) : Math.ceil( draggable_inset_y / gridy );


	// Finalized offsets
	target.offset = {
		top: (target.y * gridy + droppable_inset.top),
		left: (target.x * gridx + droppable_inset.left)
	};

	return target;
}

/**
*	Function to display a hit/miss icon
*/
Battleship.prototype.setImage = function( y, x, hit, ship, player )
{
	if( player != this.enemy.id && player != this.player.id )
		return;

	var image = hit ? $('#img #misc .hit').clone() : $('#img #misc .miss').clone();
	var grid = ( player == this.player.id ) ? $('#player div.grid div.valid') : $('#enemy div.grid div.valid');
	var grid_inset = grid.offset();

	image.css({
		position: 'absolute',
		left: x * 30 + grid_inset.left,
		top: y * 30 + grid_inset.top
	})
	.appendTo( grid )
	.show();


	if( hit && player == this.player.id )
	{
		this.shiptypes[ ship ][1]--;
		if( this.shiptypes[ ship ][1] < 1 )
		{
			$.post('battleship.php',
				{
					game:		this.gameId,
					user:		this.player.id,
					action:		'program.sinkShip(\'' + player + '\',\'' + this.player.grid[ y ][ x ] + '\')'
				}
			);
		}
	}

	if( player == this.player.id )
		this.startTurn();
}

/**
*	When whole ship is hit, show it using changed graphics
**/
Battleship.prototype.sinkShip = function( player, ship )
{
	if( player != this.enemy.id && player != this.player.id )
		return;

	alert(
		( player == this.player.id ? 'Enemy sunk your ' : 'You sunk their ' ) +
		this.shiptypes[ ship ][0]
	);

	if( player == this.player.id )
	{
		this.player.ships--;
		if( this.player.ships < 1 )
		{
			$.post('battleship.php',
				{
					game:		this.gameId,
					user:		this.player.id,
					action:		'program.victoryCheck(\'' + this.player.id + '\')'
				}
			);
		}
	}
	else if( player == this.enemy.id )
	{
		this.enemy.ships--;
		if( this.enemy.ships < 1 )
		{
			$.post('battleship.php',
				{
					game:		this.gameId,
					user:		this.player.id,
					action:		'program.victoryCheck(\'' + this.enemy.id + '\')'
				}
			);
		}
	}
}

/**
*	Check for victory
**/
Battleship.prototype.victoryCheck = function( loser )
{
	if( loser == this.player.id && this.player.ships < 1 )
	{
		$.post('battleship.php',
			{
				game:		this.gameId,
				user:		this.player.id,
				action:		'program.victoryResponse( true, \'' + this.player.id + '\', \'' + this.player.id + '\')'
			}
		);
	}
	else if( loser == this.enemy.id && this.enemy.ships < 1 )
	{
		$.post('battleship.php',
			{
				game:		this.gameId,
				user:		this.player.id,
				action:		'program.victoryResponse( true, \'' + this.enemy.id + '\', \'' + this.player.id + '\')'
			}
		);
	}
	else
	{
		$.post('battleship.php',
			{
				game:		this.gameId,
				user:		this.player.id,
				action:		'program.victoryResponse(false)'
			}
		);
	}
}

/**
*	Shoot for victory
**/
Battleship.prototype.victoryResponse = function( vic, loser, responder )
{
	if( vic )
	{
		if( responder == this.player.id )
			this.player.wins = loser;
		else if( responder == this.enemy.id )
			this.enemy.wins = loser;

		if( this.player.wins == this.enemy.wins && this.gameState == 'battle' )
			this.endGame( loser );
	}
	else
	{
		this.player.wins = false;
		this.enemy.wins = false;
	}
}

/**
*	Evreyone confirmed, end the game.
*/
Battleship.prototype.endGame = function( loser )
{
	var who = loser == this.enemy.id ? 'You win!' : 'The Enemy has won!';
	alert( who + ' Game over!' );
	this.endTurn();
	Meteor.disconnect();
	this.gameState = 'end';
}

/**
*	gets a valid grid target
**/
Battleship.prototype.getTarget = function( elem, e )
{
	var target = elem;

	//Grid sizes
	var gridx = 30;
	var gridy = 30;
	
	//Grid div calcs
	var target_inset = $(target).offset();

	// Ship Image calcs
	var inset_x = ( e.clientX - target_inset.left );
	var inset_y = ( e.clientY - target_inset.top );

	// Besure we have no negatives
	if(	inset_x < 0 )
		return false;
	if(	inset_y < 0 )
		return false;		

	// "Grid" location calulations - gives a soft leniency of 5px overhanging
	var points = {
		x: ( inset_x % gridx ) < 28 ? Math.floor( inset_x / gridx ) : Math.ceil( inset_x / gridx ),
		y: ( inset_y % gridy ) < 28 ? Math.floor( inset_y / gridy ) : Math.ceil( inset_y / gridy )
	};
	this.fireShot( this.enemy.id, points );
}

/**
*	Used to change a ship from horizontal to vertical
*	during drag operation
*	must use 'this' instead of 'this'
**/
Battleship.prototype.rotateImage = function( e )
{
	if( e.which == 92 )
	{
		//Update the image source
		var source = $(this.dragged).attr('src');
		var sclass = $(this.dragged).attr('class').substring(0,2);
		var to_replace = this.shiptypes[ sclass ][3] ? 'v-' + this.shiptypes[ sclass ][2] : this.shiptypes[ sclass ][2];
		var replace_with = this.shiptypes[ sclass ][3] ? this.shiptypes[ sclass ][2] : 'v-' + this.shiptypes[ sclass ][2];
		source = source.replace( to_replace, replace_with );

		$(this.dragged).attr({ 'src' : source }).css({
			'width': $(this.dragged).css('height'),
			'height': $(this.dragged).css('width')
		});
		
		this.shiptypes[ sclass ][3] = this.shiptypes[ sclass ][3] ? false : true;
	}

	return e;	
}