function my_DropFunc() {
	var obj = dd.obj;

	var dropTargetCell = obj.getEltBelow();
	// if dropped on an piece then check below that to find a cell.
	while(dropTargetCell != null && dropTargetCell.isCell!=true) {
		dropTargetCell = dropTargetCell.getEltBelow();
	}

	var moveIsValid = false;
	if(dropTargetCell != null) {
		// When dropping an object onto a cell, move the object so it is positioned properly within
		// the cell.
		var newX = dropTargetCell.x + obj.cellOffsetX;
		var newY = dropTargetCell.y + obj.cellOffsetY;
		obj.moveTo(newX,newY);
		obj.origX = newX;
		obj.origY = newY;

		makeMove(obj.name,dropTargetCell.name);

		moveIsValid = true;
	}

	if(!moveIsValid) {
		// reset to the object's original position
		obj.moveTo(obj.origX,obj.origY);
	}
}

function my_PickFunc() {
	var obj = dd.obj;
	if(obj==null)
		return null;
	if(obj.isButton == null || obj.isButton!=true)
		return null;
	makeMoveClick(obj.name);
}

function updateBoard(xml) {
	if(xml==null)
		return;
	var gameElem = xml.getElementsByTagName('game')[0];
	var newTurnNum = parseInt(gameElem.getAttribute('turn'));
	// If a call to updateBoard occurs while a move is being made then it might be possible for
	// old data to be passed to this method. So don't display things if we're past that turn.
	if(newTurnNum < turnNum) {
		return;
	}
	turnNum = newTurnNum;

	var turnNumStr = turnNum;
	if(turnNum==0)
		turnNumStr = 'Not yet started.';
	document.getElementById('turnNum').innerHTML = turnNum;

//	var currentPlayerNum;
	var players = gameElem.getElementsByTagName('player');
	for(var ix=0; ix<players.length; ++ix) {
		var player = players[ix];
		var num = parseInt(player.getAttribute('num'));
		var isCurrent = player.getAttribute('isCurrent');
		if(isCurrent=='true') {
			currentPlayerNum = num;
//			document.getElementById('playerCur'+ num).innerHTML = ' <b>(CURRENT)</b>';
			document.getElementById('playerCurBox'+ num).className = 'playerCurBoxCurrentPlayer';
		} else {
//			document.getElementById('playerCur'+ num).innerHTML = '';
			document.getElementById('playerCurBox'+ num).className = 'playerCurBoxNotCurrentPlayer';
		}
	}


	var boardElems = gameElem.getElementsByTagName('board');
	// If there isn't a board element then there's nothing else to do.
	if(boardElems==null)
		return;

	var boardElem = boardElems[0];
	var cells = gameElem.getElementsByTagName('cell');
	var pieces = gameElem.getElementsByTagName('piece');
	var buttons = gameElem.getElementsByTagName('but');
	var boardName = boardElem.getAttribute('name');

	processIcons(cells);
	processIcons(pieces);
	processIcons(buttons);

//logLineTo('log','updateBoard 1');
	var msgs = gameElem.getElementsByTagName('msg');
	if(msgs!=null) {
//logLineTo('log','updateBoard msgs msgs.length='+ msgs.length);
		for(var ix=0; ix<msgs.length; ++ix) {
//logLineTo('log','updateBoard msgA='+ msgId);
			var msg = msgs[ix];
			var msgId = parseInt(msg.getAttribute('id'));

			if(msgId > mostRecentMessageId) {
//logLineTo('log','updateBoard msgB='+ msgId);
				var msgStr = msg.getAttribute('msg');
				//var turn = parseInt( msg.getAttribute('turn') );
				addMessage(msgStr);
				mostRecentMessageId = msgId;
			}
		}
	}

	var chatMsgs = gameElem.getElementsByTagName('chat');
	if(chatMsgs!=null) {
//logLineTo('log','updateBoard chatMsgs chatMsgs.length='+ chatMsgs.length);
		for(var ix=0; ix<chatMsgs.length; ++ix) {
//logLineTo('log','updateBoard msgA='+ msgId);
			var msg = chatMsgs[ix];
			var msgId = parseInt(msg.getAttribute('id'));
			var playerNum = msg.getAttribute('pnum');

			if(msgId > mostRecentChatMessageId) {
//logLineTo('log','updateBoard msgB='+ msgId);
				var msgStr = msg.getAttribute('msg');
				addChatMessage(msgStr, playerNum==viewingPlayer );
				mostRecentChatMessageId = msgId;
			}
		}
	}

//	logLineTo('log','updateBoard END turn='+ turnNum);
}

function processIcons(pieces) {
	if(pieces==null)
		return;
	for(var ix=0; ix<pieces.length; ++ix) {
		var piece = pieces[ix];
		var iconName = piece.getAttribute('name');

		var obj = dd.elements[iconName];
		if(obj!=null) {
			var isVisible = (piece.getAttribute('vis') != '0');
			var isMoveable = (piece.getAttribute('move') != '0');
//			var isClickable = (piece.getAttribute('clk') == '1');

			obj.setDraggable(isMoveable);

			if(isVisible != obj.visible) {
				if(isVisible)
					obj.show();
				else
					obj.hide();
			}

			if(isVisible) {
				var imageSrc = piece.getAttribute('src');
				var iconElem = document.getElementsByName(iconName)[0];
				obj.swapImage(imageSrc);

				var cellName = piece.getAttribute('cell');
				if(cellName!=null) {
					var xOffset = parseInt(piece.getAttribute('ox'));
					var yOffset = parseInt(piece.getAttribute('oy'));
					moveIconToCell(cellName, iconName, xOffset, yOffset, pieceMovementVelocity);
				}
			}
		}

	}
}

function makeMove(pieceName,destCellName) {
//logLineTo('log','makeMove');
	var userCookie = getCookie('user');
	if(userCookie==null && viewingPlayerEmail==null) {
//TODO: user isn't logged in, so send to login.
		return;
	}
	var callback = function(xml) {
		updateBoard(xml);
		watchForMove(watchForMoveWaitTime);
	}
	var qstr =
		'act=move'
		+'&gameId='+ gameId
		+'&userCookie='+ escape(userCookie)
		+'&mostRecentMessageId='+ mostRecentMessageId
		+'&mostRecentChatMessageId='+ mostRecentChatMessageId
		+'&pieceName='+ escape(pieceName)
		+'&destCellName='+ escape(destCellName)
		;
	if(viewingPlayerEmail!=null)
		qstr += '&email='+ escape(viewingPlayerEmail);
//logLineTo('log','qstr='+ qstr);
	xmlHttpPost2('/know/game/MakeGameMove',qstr,true,callback);
}

function makeMoveClick(cellName) {
//logLineTo('log','makeMove');
	var userCookie = getCookie('user');
	if(userCookie==null && viewingPlayerEmail==null) {
//TODO: user isn't logged in, so send to login.
		return;
	}
	var callback = function(xml) {
		updateBoard(xml);
		watchForMove(watchForMoveWaitTime);
	}
	var qstr =
		'act=click'
		+'&gameId='+ gameId
		+'&userCookie='+ escape(userCookie)
		+'&mostRecentMessageId='+ mostRecentMessageId
		+'&mostRecentChatMessageId='+ mostRecentChatMessageId
		+'&cellName='+ escape(cellName)
		;
	if(viewingPlayerEmail!=null)
		qstr += '&email='+ escape(viewingPlayerEmail);
//logLineTo('log','makeMoveClick()="'+ cellName +'"');
	xmlHttpPost2('/know/game/MakeGameMove',qstr,true,callback);
}

function watchForMove(nextWaitTime) {
//logLineTo('log','watchForMove 1');
	// don't start watching again if we're already in the middle of a watch call.
	if(watchForMoveIsRunning)
		return;
//	logLineTo('log','watchForMove turn='+ turnNum +' currentPlayerNum='+ currentPlayerNum +' viewingPlayer='+ viewingPlayer);

	// If the player is the current player then there's no need to watch for moves.
//		if(currentPlayerNum == viewingPlayer) {
//			logLineTo('log','watchForMove (currentPlayerNum==viewingPlayer)='+ (currentPlayerNum==viewingPlayer) );
//			return;
//		}

	watchForMoveIsRunning = true;
	var callback = function(xml) {
		if(xml!=null)
			updateBoard(xml);

		// If, after updating the board, we find that the viewing player is not the current
		// player then continue to watch the board.
//		logLineTo('log','(currentPlayerNum != viewingPlayer)='+ (currentPlayerNum != viewingPlayer) +' watchForMoveAllowed='+ watchForMoveAllowed);
		if(currentPlayerNum != viewingPlayer && watchForMoveAllowed==true) {
			var watchForMoveCall = 'watchForMove('+ (nextWaitTime*waitTimeIncreasePct) +')';
//logLineTo('watchForMoveCall='+ watchForMoveCall);
			setTimeout(watchForMoveCall,nextWaitTime);
		}
		watchForMoveIsRunning = false;

		// this watches again regardless of whether this player is the current player or not.
		watchForMove(watchForMoveWaitTime);
	}
	var userCookie = getCookie('user');
	if(userCookie==null && viewingPlayerEmail==null) {
//TODO: user isn't logged in, so send to login.
		return;
	}
	var qstr =
		'gameId='+ gameId
		+'&userCookie='+ escape(userCookie)
		+'&turnNum='+ turnNum
		+'&viewingPlayer='+ viewingPlayer
		+'&mostRecentMessageId='+ mostRecentMessageId
		+'&mostRecentChatMessageId='+ mostRecentChatMessageId
		;
	if(viewingPlayerEmail!=null)
		qstr += '&email='+ escape(viewingPlayerEmail);
//logLineTo('log','qstr='+ qstr);
	xmlHttpPost2('/know/game/WatchForMove',qstr,true,callback);
}

function sendChatMessage(msgInput) {
/*	if(true) {
		alert('msg sent msg='+ msgInput.value);
		return;
	}
*/
	var msg = msgInput.value;
	// clear the input box.
	msgInput.value = '';

//logLineTo('log','makeMove');
	var userCookie = getCookie('user');
	if(userCookie==null && viewingPlayerEmail==null) {
//TODO: user isn't logged in, so send to login.
		return;
	}
	var callback = function(xml) {
		// Updating the board also means updating the list of chat messages. We want to show the user the message they just sent.
		if(xml!=null)
			updateBoard(xml);
	}
	var qstr =
		'act=chat'
		+'&gameId='+ gameId
		+'&userCookie='+ escape(userCookie)
		+'&msg='+ escape(msg)
		+'&mostRecentMessageId='+ mostRecentMessageId
		+'&mostRecentChatMessageId='+ mostRecentChatMessageId
		;
	if(viewingPlayerEmail!=null)
		qstr += '&email='+ escape(viewingPlayerEmail);
//logLineTo('log','qstr='+ qstr);
	xmlHttpPost2('/know/game/MakeGameMove',qstr,true,callback);
}

// Returns true if the user just pressed enter.
function keypressWasEnter(event) {
	if(event==null)
		return false;
	var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
//	logLineTo('log','event keyCode='+ keyCode);
	return keyCode == 13;
}
window.captureEvents(Event.KEYPRESS);

/* If movementVelocity is null then just instantly appears in the cell. Otherwise it moves at the
specified velocity (measured in pixels per second). */
function moveIconToCell(cellName, iconName, xOffset, yOffset, movementVelocity) {
	var cell = dd.elements[cellName];
	var obj = dd.elements[iconName];
	if(cell==null || obj==null)
		return;
	var newX = cell.x + xOffset;
	var newY = cell.y + yOffset;

//	// If the current position isn't known then we can't properly move it, so just instantly move the piece.
//	var origX = obj.origX;
//	var origY = obj.origY;
//	if(origX==null || origY==null)
//		movementVelocity = null;

	if(movementVelocity==null || movementVelocity <= 0) {
		// move instantly to destination.
		obj.moveTo(newX,newY);
	} else {
		if(obj.x != newX || obj.y != newY)
			moveObjWithVelocity(iconName,newX,newY,movementVelocity);
	}

	obj.origX = newX;
	obj.origY = newY;
	obj.cellOffsetX = xOffset;
	obj.cellOffsetY = yOffset;
	return obj;
}

function moveObjWithVelocity(iconName,newX,newY,movementVelocity) {
	var obj = dd.elements[iconName];

	// If 10 then that means that 10 moves per second are made until the destination is reached.
	var moveTimePart = pieceMovementTimeSlice;
	var velocity = movementVelocity / moveTimePart;

	var curX = obj.x;
	var curY = obj.y;
	var xMove = newX - curX;
	var yMove = newY - curY;
	var r = Math.sqrt( xMove*xMove + yMove*yMove );
//logLineTo('log','moveObjWithVelocity iconName="'+ iconName +'" curX='+ curX +' curY='+ curY +' newX='+ newX +' newY='+ newY +' movementVelocity='+ movementVelocity);
	if(r < 0.5)
		return;

	var vx = velocity * xMove / r;
	var vy = velocity * yMove / r;
	var movingLeft = (xMove < 0);
	var movingUp = (yMove < 0);
//logLineTo('log','moveObjWithVelocity 10  curX='+ curX +' curY='+ curY +' vx='+ vx +' vy='+ vy +' r='+ r);
	curX += vx;
	curY += vy;
//logLineTo('log','moveObjWithVelocity 11  curX='+ curX +' curY='+ curY +' vx='+ vx +' vy='+ vy +' r='+ r);

	if(movingLeft) {
		if(curX < newX)
			curX = newX;
	} else {
		if(curX > newX)
			curX = newX;
	}

	if(movingUp) {
		if(curY < newY)
			curY = newY;
	} else {
		if(curY > newY)
			curY = newY;
	}

//logLineTo('log','moveObjWithVelocity 20');
	obj.moveTo(curX,curY);

//logLineTo('log','moveObjWithVelocity 25 Math.abs(curX-newX)='+ (Math.abs(curX-newX)) +' Math.abs(curY-newY)='+ (Math.abs(curY-newY)));
	if(Math.abs(curX-newX) < 0.5 && Math.abs(curY-newY) < 0.5) {
//logLineTo('log','moveObjWithVelocity 30');
		return;
	} else {
		var call = "moveObjWithVelocity('"+ iconName +"',"+ newX +","+ newY +","+ movementVelocity +")";
//logLineTo('log','moveObjWithVelocity 40');
		setTimeout(call,1000 / moveTimePart);
//logLineTo('log','moveObjWithVelocity 42 moveTimePart='+ moveTimePart);
	}
}

//function addPiece(cellName, iconName, xOffset, yOffset, imgSrc) {
//}
