var latestMessagePos = -1;
var room = 'main';
var userid;
var initialPeriod = 4;
var period = initialPeriod;
var countdown = 0;
var chatDiv;
var waitingForResponse = false;
var scheduledPosts = new Array();
var messageCount = 0;
var cubeHolder = null;
var lastMoveIdx = -1;
var cubeReleaseRequested = false;
var users = new Array();

var req, done;
function sendRequest(url, postData, callback) {
	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		try {
			req = new XMLHttpRequest();
		} catch(e) {
			req = false;
		}
	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		try {
			req = new ActiveXObject("Msxml2.XMLHTTP");
		} catch(e) {
			try {
				req = new ActiveXObject("Microsoft.XMLHTTP");
			} catch(e) {
				req = false;
			}
		}
	}

	if (req) {
		done = false;
		req.onreadystatechange = function() {
			if (req.readyState == 4 && req.status == 200 && !done) {
				done = true;
				callback();
			}
		}
		req.open("POST", url, true);
		req.send(postData);
	}
}

function splitLines(text) {
	var outLines = new Array();
	var lines = text.split('\n');
	for (var i = 0; i < lines.length; i++) {
		var line = lines[i];
		if (!(line.length == 0 && i == lines.length-1))
			outLines.push(line);
	}
	return outLines;
}

function el(type, attributes, content) {
	var element = document.createElement(type);
	if (attributes) {
		var attrs = attributes.split(',');
		for (var i = 0; i < attrs.length; i++)
		{
			var fields = attrs[i].split('=');
			if (fields[0] == 'class')
				element.className = fields[1];
			else if (fields[0] == 'onclick')
			{
				element.onclick = function(e) { eval(fields[1]); };
				if (element.captureEvents) element.captureEvents(Event.CLICK);
			}
			else
				element.setAttribute(fields[0], fields[1]);
		}
	}
	if (content) {
		element.appendChild(content);
	}
	return element;
}

function tx(text)
{
	return document.createTextNode(text);
}

function eResponse()
{
	var messages = splitLines(req.responseText);
	for (var i = 0; i < messages.length; i++) {
		var fields = messages[i].split('\^');
		var pos = fields[0];
		var uid = fields[1];
		var textf = fields[2];

		var colonIdx = textf.indexOf(':');
		var msgType;
		var msg;
		if (colonIdx >= 0) {
			msgType = textf.substring(0, colonIdx);
			msg = textf.substring(colonIdx+1);
		}
		else {
			msgType = 't';
			msg = textf;
		}


		if (latestMessagePos < 0) {
			// We use the first message to get the latest message pos, and the current state of the cube

			var stateFields = msg.split(':');
			setCubeState(stateFields[0]);
			//displayTextMessage(pos,uid,'setCubeState to '+stateFields[0]);

			cubeHolder = (stateFields[1] != null && stateFields[1].length > 0) ? stateFields[1] : null;
			if (cubeHolder == userid)
				try { document.CubeSim.unlock(); } catch (e) {}
			updateCubeFooter();

			postMessage(userid+' has joined the chatroom');
		}
		else {
			//displayTextMessage(pos,uid,msgType+':'+msg);
			if (msgType == 't')
				displayTextMessage(pos,uid,msg);
			else if (msgType == 'm')
				performCubeMoves(uid,msg);
			else if (msgType == 'g')
				cubeGrabbed(uid);
			else if (msgType == 'r')
				cubeReleased(uid);
			else if (msgType == 'h') // someone introduces themselves to you.
				addUser(uid);
			else if (msgType == 'b') // someone says bye
				removeUser(uid);
		}

		latestMessagePos = pos;
	}
	waitingForResponse = false;
}

function displayTextMessage(pos, uid, text)
{
	var html;
	if (text.match(/ has joined the chatroom/))
	{
		html = text;
		addUser(uid); // remember the user
		scheduledPosts.push('h:'); // say hi to the new user
	}
	else if (text.match(/ has left the chatroom/))
	{
		html = text;
	}
	else
		html = uid+': '+text;

	var chatDiv = document.getElementById('chatDiv');
	var messageDiv = document.getElementById('message_'+pos);
	if (messageDiv) chatDiv.removeChild(messageDiv);
	messageDiv = el('div', 'id=message_'+pos+',class='+(messageCount%2==0?'even':'odd'), tx(html));
	messageCount++; // do not count posts that contain cube moves
	chatDiv.appendChild(messageDiv);
	chatDiv.scrollTop = chatDiv.scrollHeight;
}

function setCubeState(state)
{
	try { document.CubeSim.setCubeState(state); } catch (e) {}
	lastMoveIdx = -1;
}

function performCubeMoves(uid, moves)
{
	if (uid != cubeHolder)
		return;

	lastMoveIdx += moves.split(',').length;
	if (uid != userid)
		try { document.CubeSim.performMoves(moves); } catch (e) {}
}

function cubeGrabbed(uid)
{
	if (uid == userid)	// if I grabbed the cube
		try { document.CubeSim.unlock(); } catch (e) {}
	else if (cubeHolder == userid)	// if someone drabbed the cube off me
	{
		try { document.CubeSim.lock(); } catch (e) {}
		// reboot and grab the correct state of the cube
		alert("The cube has been grabbed from you by "+uid+". Will attempt to reinitialise the chat session.");
		scheduledPosts = new Array();
		latestMessagePos = -1;
		lastMovePos = -1;
		return;
		// Things should automatically start over on the next doCycle()
	}
	cubeHolder = uid;
	updateCubeFooter();
}

function removeUser(uid)
{
	for (var i = 0; i < users.length; i++)
		if (uid == users[i])
		{
			users.splice(i, 1);
			updateUsersDiv();
			return;
		}
}

function addUser(uid)
{
	for (var i = 0; i < users.length; i++)
		if (uid == users[i])
			return;
	users.push(uid);
	updateUsersDiv();
}

function updateUsersDiv()
{
	var usersDiv = document.getElementById('usersDiv');
	usersDiv.innerHTML = "Online: " + users.join(', ');
}

function updateCubeFooter()
{
	var cubeFooter = document.getElementById('cubeFooter');
	if (cubeReleaseRequested)
		cubeFooter.innerHTML = "Releasing cube...";
	else if (cubeHolder == userid)
		cubeFooter.innerHTML = 'You have the cube. <input type="button" value="Release cube" onclick="releaseCube();">';
	else if (cubeHolder != null)
		cubeFooter.innerHTML = cubeHolder+' has the cube. <input type="button" value="Grab cube" onclick="grabCube();">';
	else
		cubeFooter.innerHTML = '<input type="button" value="Grab cube" onclick="grabCube();">';
}

function cubeReleased(uid)
{
	if (uid != cubeHolder)
		return;
	cubeHolder = null;
	updateCubeFooter();
}

// public
function postMessage(text)
{
	if (text == null || text.length == 0)
		return;
	period = initialPeriod;
	countdown = 0;
	scheduledPosts.push('t:'+text);
}

function postCubeMoves(moves)
{
	period = initialPeriod;
	countdown = 0;
	scheduledPosts.push('m:'+moves);
}

// public
function grabCube()
{
	if (cubeHolder != userid)
	{
		if (cubeHolder != null)
		{
			if (!confirm("Are you sure you wish to take the cube from "+cubeHolder+"? (This should only be done if "+cubeHolder+" forgot to release the cube when leaving the chat room. If you do it in any other situation, the chat state may become inconsistent and this program may stop working for everyone.)"))
				return;
		}
		// disable button.
		period = initialPeriod;
		countdown = 0;
		scheduledPosts.push('g:');
		//Wait for the eResponse to tell us whether we succeeded in taking the cube.
	}
}

// public
function releaseCube()
{
	if (cubeHolder == userid)
	{
		try { document.CubeSim.lock(); } catch (e) {}
		cubeReleaseRequested = true;
		period = initialPeriod;
		countdown = 0;
	}
	else
	{
		alert("Error: you cannot release the cube since you do not have it.");
	}
}

function postMovesFromCube()
{
	try {
		var moves = ''+document.CubeSim.getMovesSince(lastMoveIdx);
		if (moves.length > 0)
		{
			postCubeMoves(moves);
			//This is handled in the eResponse
			//lastMoveIdx += moves.length;
			return true;
		}
		else
		{
			return false;
		}
	}
	catch (e) {
		return false;
	}
}

function sendScheduledPosts()
{
	var posts = scheduledPosts;
	scheduledPosts = new Array();
	if (posts.length > 0)
	{
		var postData = '';
		for (var i = 0; i < posts.length; i++)
			postData += posts[i]+"\n";
		sendChatRequest(postData);
		return true;
	}
	else
	{
		return false;
	}
}

function setUserid(nuserid)
{
	if (! /^[a-zA-Z0-9 ]+$/.test(nuserid))
	{
		alert('Name must contain only characters a-z,A-Z,0-9,_ and space');
		return;
	}

	userid = nuserid;
	var nameRow = document.getElementById('nameRow');
	nameRow.style.display = 'none';
	var messageRow = document.getElementById('messageRow');
	messageRow.style.display = 'block';

	doCycle();
}

function sendChatRequest(postData)
{
	waitingForResponse = true;
	sendRequest('http://www.ryanheise.com/cube/chat/e.cgi?r='+room+'&u='+escape(userid)+'&s='+latestMessagePos, postData, eResponse);
}

function checkMessages()
{
	sendChatRequest("\n");
}

function doCycle()
{
	if (countdown <= 0 && !waitingForResponse)
	{
		postMovesFromCube();
		if (cubeReleaseRequested)
		{
			scheduledPosts.push('r:');
			cubeReleaseRequested = false;
		}
		if (sendScheduledPosts())
		{
			period = initialPeriod;
		}
		else
		{
			checkMessages();
			//period *= 2;
			//period = 2;
			period++;
		}
		countdown = period;
	}
	else
	{
		countdown--;
	}
	setTimeout("doCycle();", 500);
}

function initChat(lroom) {
	room = lroom;
	userid = 'guest' + Math.floor(Math.random()*1000);
	window.onunload = goodbye;
}

function wakeUp() {
	period = initialPeriod*2;
	if (countdown > period)
		countdown = period;
}

function goodbye() {
	// should be called on page unload.
	if (latestMessagePos >= 0)
		sendChatRequest("t:"+userid+" has left the chatroom\nb:\n"); // say bye
}

function reset() {
	try { document.CubeSim.lock(); } catch (e) {}
	scheduledPosts = new Array();
	latestMessagePos = -1;
	lastMovePos = -1;
	period = initialPeriod;
	countdown = 0;
}
