var req;
var sentTime = 0;
var up = false;
var down = false;
var paddle1;
var paddle2;
var ball;
var field;
var sid;
var currentPlayer;
var pingQueue = new Queue();
var updateBall = false;
var lastInputTime = 0;

var debugOn;
var d1;
var d2;
var d3;
var d4;
var d5;
var d6;
var d7;
var d8;
var d9;
var startText;

function Connection()
{
	var req;

	this.canStart = function()
	{
		if(req.readyState == 4)
		{
			var response = req.responseText;
			debug(d6,  'response:'+ req.responseText);

			switch(response)
			{
				case "busy":
					setText(" A game is in progress. Try again in a few minutes");
					break;
				case "startp1":
					setText(" <- You are player 1. Go!");
					currentPlayer = 1;
					game.startConnection();
					break;
				case "startp2":
					setText("Waiting for player 1. You are player 2 ->");
					currentPlayer = 2;
					game.startConnection();
					break;
				default:
					setText(" Error starting game.");
					break;
					
			}
		}
	}

	this.handleUpdate = function()
	{
		if(req.readyState == 4)
		{
			var now = new Date();
			var gameState = req.responseText;

			pingQueue.addItem(now.getTime() - sentTime);
			//debug(d4,  'queue:'+pingQueue.toString());
			debug(d5,  'ping:'+pingQueue.average());
			debug(d6,  'response:'+ req.responseText);

			var response = req.responseText.split(':');
			time = roundNum(response[0] * 100 + pingQueue.average() / 2);
			gameState = response[1];

			debug(d9,  "totaltime:"+time);

			if(time > 2000)
				return;

			if(gameState.indexOf(',') > -1)
			{
				var ballPos = gameState.split(',');
				var pos = parseInt(ballPos[0]);
				ball.setState(parseFloat(ballPos[1]), parseFloat(ballPos[2]),
						parseFloat(ballPos[3]), parseFloat(ballPos[4]),
						parseFloat(ballPos[5]), time);

				return;
			}
			else
				var pos = parseInt(gameState);

			if(pos == "NaN")
				pos = 0;

			if(currentPlayer == 1)
				paddle2.setPos(pos);
			else
				paddle1.setPos(pos);
		}
	}

	this.send = function(type, message, callback)
	{
		if(isIE())
			req = new ActiveXObject("Microsoft.XMLHTTP");
		else
			req = new XMLHttpRequest();

		debug(d7,  message);

		req.open('get', './pong.php?'+ message);
		switch(type)
		{
			case "start":
				req.onreadystatechange = this.canStart;
				break;
			case "update":
				req.onreadystatechange = this.handleUpdate;
				break;
		}
		req.send(null);

		sentTime = new Date().getTime();
	}
}

function PongGame()
{
	this.connection = new Connection();
	this.playerInterval;
	this.updateInterval;
	this.ballInterval;

	this.setup = function()
	{
		//sid = Math.round(Math.random() * 1000000000);
		//debug(d5,  'id:'+sid);
		if(! isIE())
		{
			document.captureEvents(Event.KEYDOWN);
			document.captureEvents(Event.KEYUP);
		}

		document.onkeydown = keyDown;
		document.onkeyup = keyUp;

		document.write('<div class="box" id="field"><img class="paddle" id="paddle_1" src="trans.gif" /><img class="paddle" id="paddle_2" src="trans.gif" /><img class="ball" id="ball" src="trans.gif" /><img class="ball" id="mark1" src="trans.gif" /><img class="ball" id="mark2" src="trans.gif" /></div>');

		field = new Field(document.getElementById('field'));
		ball = new Ball(document.getElementById('ball'));
		paddle1 = new Paddle(document.getElementById("paddle_1"), 1)
		paddle2 = new Paddle(document.getElementById("paddle_2"), 2)

			//setInterval('paddle1.update()', 20);
			//setInterval('paddle2.update()', 20);
	}

	this.start = function()
	{
		this.connection.send("start", "start=1", '');
	}

	this.startConnection = function()
	{
		lastInputTime = new Date().getTime();

		this.ballInterval = setInterval('ball.move()', 50);

		if(currentPlayer == 1)
			this.playerInterval = setInterval('paddle1.update()', 20);
		else
			this.playerInterval = setInterval('paddle2.update()', 20);

		if(currentPlayer == 1)
			this.updateInterval = setInterval('game.updateServer()', 100);
		else
			this.updateInterval = setInterval('game.updateServer()', 100);

		if(currentPlayer == 1)
		{
			ball.vel[0]+=-2;
			ball.vel[1]+=0;
			ball.updateServer();
		}
	}

	this.updateServer = function()
	{
		var message = "";
		var paddlePos;

		if(this.notActive())
		{
			setText("No input for 60 seconds. Timing-out.");
			clearInterval(this.ballInterval);
			clearInterval(this.playerInterval);
			clearInterval(this.updateInterval);

			return;
		}

		if(currentPlayer == 1)
			paddlePos = paddle1.y;
		else
			paddlePos = paddle2.y;

		message = message + currentPlayer + ":" + roundNum(pingQueue.average() / 100 / 2) + ":" + paddlePos;

		if(updateBall)
		{
			updateBall = false;
			message = message + ',' + ball.toString();
			//debug(d8,  'updateball:' + ball.toString());
		}
		
		this.connection.send("update", "plr="+ message, 'paddle1.setPos');
	}

	this.notActive = function()
	{
		return new Date().getTime() - lastInputTime > 60000;
	}
}


function keyUp(e)
{
	var key = e? e.which : window.event.keyCode;

	switch(key)
	{
		case 87:
		case 81:
		case 38:
			up = false;
			break;
		
		case 83:
		case 65:
		case 40:
			down = false;
			break;
	}
}

function keyDown(e)
{
	var key = e? e.which : window.event.keyCode;
	
	switch(key)
	{
		case 87:
		case 81:
		case 38:
			up = true;
			down = false;
			break;
		
		case 83:
		case 65:
		case 40:
			down = true;
			up = false;
			break;
	}
}

function isIE()
{
	if(navigator.userAgent.indexOf('MSIE') > -1 && navigator.userAgent.indexOf('Opera') < 0)
		return true;
	return false;
}

function isSafari()
{
	if(navigator.userAgent.indexOf('Safari') > -1)
		return true;
	return false;
}

// ----- field -----

function Field(obj)
{
	this.field = obj;
	this.width = 250;
	this.height = 150;

	this.field.style.height = this.height + "px";
	this.field.style.width = this.width + "px";
	this.field.style.position = "relative";
	this.field.style.border = "1px black solid";
}

// ----- paddle -----

function Paddle(obj, pNum)
{
	this.paddle = obj;
	this.x = 0;
	this.y = 0;
	this.width = 5;
	this.height = 30;
	this.score = 0;
	this.playerNumber = pNum;

	if(this.playerNumber == 1)
		this.x = 0;
	else
		this.x = field.width - this.width;

	this.paddle.style.position = "absolute";
	this.paddle.style.left = this.x + "px";
	this.paddle.style.top = this.y + "px";
	this.paddle.style.width = this.width + "px";
	this.paddle.style.height = this.height + "px";
	this.paddle.style.backgroundColor = "blue";

	this.moveUp = function()
	{
		this.setPos(this.y-1);
		lastInputTime = new Date().getTime();
	}

	this.moveDown = function()
	{
		this.setPos(this.y+1);
		lastInputTime = new Date().getTime();
	}

	this.setPos = function(newY)
	{
		this.y = newY;
		
		if(this.y < 0)
			this.y = 0;

		if(this.y > field.height - this.height)
			this.y = field.height - this.height;

		this.paddle.style.top = this.y + "px";
	}

	this.update = function()
	{
		if(up)
		{ this.moveUp(); }
		else if(down)
		{ this.moveDown(); }
	}

	this.hit = function(top, bottom)
	{
		if (bottom > this.y && bottom < this.y + this.height ||
			top > this.y && top < this.y + this.height )
		{
			var ball_center = (bottom - top) / 2 + top;
			var paddle_center = this.height / 2 + this.y;
			var distance = (ball_center - paddle_center) /
				this.height * 2 + 0.001;

			return distance;
		}

		return 0;
	}
}

// ----- ball -----

function Ball(obj)
{
	this.ball = obj;
	this.size = 5;
	this.speed = 1;
	this.pos = new Array(field.width/2,field.height/2);
	this.vel = new Array(0,0);
	this.max_speed = 3;

	this.ball.style.width = this.size + "px";
	this.ball.style.height = this.size + "px";
	this.ball.style.position = "absolute";
	this.ball.style.backgroundColor = "orange";
	this.lastUpdate = 0;

	this.mark1 = document.getElementById('mark1');
	this.mark2 = document.getElementById('mark2');
	this.mark1.style.width = this.size + "px";
	this.mark1.style.height = this.size + "px";
	this.mark1.style.position = "absolute";
	this.mark1.style.backgroundColor = "red";
	this.mark1.style.opacity = "0.1";
	this.mark2.style.width = this.size + "px";
	this.mark2.style.height = this.size + "px";
	this.mark2.style.position = "absolute";
	this.mark2.style.backgroundColor = "green";
	this.mark2.style.opacity = "0.1";

	this.move = function moveBall()
	{
		this.pos[0] = roundNum(this.pos[0] + this.vel[0] * this.speed);
		this.pos[1] = roundNum(this.pos[1] + this.vel[1] * this.speed);

		debug(d3,  'ball:'+Math.round(this.pos[0]) + ','+Math.round(this.pos[1]) + ' ' + Math.round(this.vel[0])+','+ Math.round(this.vel[1]) + ', ' + this.speed);

		if(this.pos[0] < paddle1.x + paddle1.width && this.vel[0] < 0)
		{
			var hit = paddle1.hit(this.pos[1], this.pos[1] + this.size)
			if(hit)
			{
				this.pos[0] = paddle1.x + paddle1.width;
				this.vel[0] = -this.vel[0];
				this.vel[1] = this.max_speed * hit;
		//		this.speed += 0.1;

				if(currentPlayer == 1)
					this.updateServer();
			}

			if(this.pos[0] < 0)
			{
				this.playerScored(paddle2);

				if(currentPlayer == 1)
					this.updateServer();
			}
		}

		if(this.pos[0] + this.size > paddle2.x && this.vel[0] > 0)
		{
			var hit = paddle2.hit(this.pos[1], this.pos[1] + this.size);
			if(hit)
			{
				this.pos[0] = paddle2.x - this.size;
				this.vel[0] = -this.vel[0];
				this.vel[1] = this.max_speed * hit;
//				this.speed += 0.1;

				if(currentPlayer == 2)
					this.updateServer();
			}

			if(this.pos[0] + this.size > field.width)
			{
				this.playerScored(paddle1);

				if(currentPlayer == 2)
					this.updateServer();
			}
		}
		if(this.pos[1] < 0)
		{
			this.pos[1] = 0;
			this.vel[1] = -this.vel[1];
		}
		if(this.pos[1] + this.size > field.height)
		{
			this.pos[1] = field.height - this.size;
			this.vel[1] = -this.vel[1];
		}

		this.ball.style.top = this.pos[1] + 'px';
		this.ball.style.left = this.pos[0] + 'px';
	}

	this.updateServer = function()
	{
		updateBall = true;
		game.updateServer();
		updateBall = true;
	}

	this.reset = function()
	{
		this.speed = 1;
		this.pos[1] = field.height / 2;

		if(this.vel[0] < 1)
			this.vel[0] = -2;
		else
			this.vel[0] = 2;
		this.vel[1] = 1;
	}

	this.setState = function(posX, posY, velX, velY, speed, time)
	{
		var d = new Date();

		if(d.getTime() - this.lastUpdate < 1000)
			return;

		this.mark1.style.top = this.pos[1] + "px";
		this.mark1.style.left = this.pos[0] + "px";
		
		this.vel[0] = velX;
		this.vel[1] = velY;
		this.pos[0] = posX;
		this.pos[1] = posY;
		this.speed = speed;

		this.moveExtra(time);

		this.lastUpdate = d.getTime();
	}

	this.moveExtra = function(time)
	{
		var extra = time / 20;

		debug(d8,  "extra:" + roundNum(time) + " " + roundNum(extra));
		this.pos[0] = this.pos[0] + this.vel[0] * this.speed * extra;
		this.pos[1] = this.pos[1] + this.vel[1] * this.speed * extra;

		this.mark2.style.top = this.pos[1] + "px";
		this.mark2.style.left = this.pos[0] + "px";
	}

	this.playerScored = function(p)
	{
		p.score++;
		this.pos[0] = p.x;
		this.reset();
	}

	this.toString = function()
	{
		return this.pos[0] + ',' + this.pos[1] + ',' + this.vel[0] + ',' + this.vel[1] + ',' + this.speed;
	}
}




function Queue()
{
	this.items = new Array(0,0,0,0,0);
	this.total = 0;

	this.addItem = function(item)
	{
		this.total -= this.items.shift();
		this.total += item;
		this.items.push(item);
	}

	this.average = function()
	{
		return roundNum(this.total / this.items.length);
	}

	this.setMaxSize = function(s)
	{
		this.items = new Array(s);
		this.total = 0;

		for(i in this.items)
			i = 0;
	}

	this.toString = function()
	{
		return this.items.toString() + " " + this.total;
	}
}

function roundNum(x)
{
	return Math.round(x*100)/100;
}

var game = new PongGame();

game.setup();

function setup()
{
	d1 = document.getElementById('d1');
	d2 = document.getElementById('d2');
	d3 = document.getElementById('d3');
	d4 = document.getElementById('d4');
	d5 = document.getElementById('d5');
	d6 = document.getElementById('d6');
	d7 = document.getElementById('d7');
	d8 = document.getElementById('d8');
	d9 = document.getElementById('d9');
	startText = document.getElementById('startText');
}

function debug(obj, value)
{
	if(debugOn)
		obj.value = value;
}

function setDebug(checkBox)
{
	debugOn = checkBox.checked;
	var d = $('debugFields');

	if(debugOn)
		d.style.display = 'inline';
	else
		d.style.display = 'none';
}

function setText(newText)
{
	if(isIE() || isSafari())
		startText.innerText = newText;
	else
		startText.textContent= newText;
}

