<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Noughts and Crosses" height="320"
	 description="An experiment to make a Noughts and Crosses game"
	author="Andrew Liles">
  <Require feature="rpc" />
</ModulePrefs>
<Content type="html">
<![CDATA[

<script type="text/javascript" src="http://wave-api.appspot.com/public/wave.js"></script>
<script language="JavaScript">
/** Synopsis:

	1. The init() is invokved upon load of the Gagdget,
		note it does not access any state as this is invalid at this time
		it merely registers 2 call backs
	2. newParticipant() is typically called first, as this requires 'state'
		at this point we just set a flag and return

	3. render() is invoked by the wave client when the state changes.
		It calls renderParticipants() and then renderGrid()

	4. renderParticipants()
		The first time around (and after any new participant) processParticipants()
		is called to assign each player a symbol: O or X or observer for the 3rd participant.
		It changes the wave state by using a 'delta'

	5. renderGrid()
		This yanks the play state of each cell from the wave state object, if there is
		a symbol it draws it, otherwise it puts in a radio button.  The radio button has
		a JavaScript play() method onClick.

	6. play()
		This create a state delta and submits it - note that it does not render the grid as
		the state is not defined until we get a state changed call back (which invokes render()).
***/


var REGISTERED_PARTICIPANT_COUNT = "rc";
var participantChangePending;

function init() {
	if (wave && wave.isInWaveContainer()) {
		wave.setStateCallback(render);
		wave.setParticipantCallback(newParticipant);
	}
	else {
		alert("Not in a wave");
	}
}

function newParticipant() {
	//console.log("newParticipant: start");
	participantChangePending = true;
	render();
}

/** Store in the state a participant symbol keyed by the participant Id
  * if a participants index is 0 then they are player "O"
  * if a participants index is 1 then they are player "X"
  * if a participants index is >1 then they are an observer.
  *
  * This cannot be done during the callback for a participant change
  * that occurs at start up since we require access to the state object
  * which at that time is not available so a flag is set so we know to do it later
  * is this REALLY the way we should do this??
  * LIMITATION: we dont support participants leaving - why would they leave such a great game?
  **/
function processParticipants() {
	//console.log("processParticipants: start");
	//do we need to do anything?
	if(!participantChangePending) {
		return;
	}

	//are there are any participants - surely there must be
	var participants = wave.getParticipants();
	if(!wave.getParticipants()) {
		//console.log("processParticipants: no participants");
		return;
	}

	var state = wave.getState();
	if(!state) {
		//console.log("processParticipants: no state");
		//alert("oops, no state??");
		//uh oh no state, so we return keeping flag set and do this job later
		return;
	}

	var delta = {};

	var existingParticipants = 0;
	if(state.get(REGISTERED_PARTICIPANT_COUNT)) {
		existingParticipants = state.get(REGISTERED_PARTICIPANT_COUNT);
	}
	//console.log("processParticipants: existingParticipants="+existingParticipants);

	//Note: we may adding >1 participant at this time
	var newParticipants = 0;
	for(var pIdx = 0; pIdx < participants.length; pIdx++) {
		if(!state.get(participants[pIdx].getId())) {
			//this participant is not registered, so register her
			var participantIndex = existingParticipants+newParticipants;
			//console.log("processParticipants: unregistered participantId=" + participants[pIdx].getId() + " index=" + participantIndex);
			var symbol = '-';
			if(participantIndex == 0) {
				symbol = "O";
			}
			if(participantIndex == 1) {
				symbol = "X";
			}
			delta[participants[pIdx].getId()] = symbol;
			newParticipants++;
		}
	}
	var newParticipantCount = existingParticipants+newParticipants;
	//console.log("processParticipants: newParticipantCount=" + newParticipantCount);
	delta[REGISTERED_PARTICIPANT_COUNT] = newParticipantCount;

	participantChangePending = null;
	//console.log("processParticipants: prior to delta call; delta=" + wave.util.printJson(delta, false, 0));
	state.submitDelta(delta);
	//console.log("processParticipants: post delta call");
}

/** get the current symbol for the participant
  * @return null if the user is not allowed to play (i.e. 3rd+ participant)
  **/
function getParticipantSymbol(participant) {
	if (!wave.getState()) {
		return;
	}
	var symbol = wave.getState().get(participant.getId());
	//console.log("For participant " + participant.getId() + " symbol=" + symbol);
	if(!symbol || (symbol == '-')) {
		return null;
	}
	return symbol;
}

/** get the current users symbol
  * @return null if the user is not allowed to play (i.e. 3rd+ participant)
  **/
function getViewerSymbol() {
	var self = wave.getViewer();
	if(self) {
		return getParticipantSymbol(self);
	}
	return null;
}

function render() {
	//console.log("render: start");
	renderParticipants();
	renderGrid();
}

function renderParticipants() {
	//console.log("renderParticipants: start");
	if (!wave.getParticipants()) {
		return;
	}
	processParticipants();

	if(wave.getState()) {
		//console.log("state=" + wave.getState().toString());
	}


	var participantInfo = document.getElementById("participantInfo");
	if(participantInfo) {
		var participants = wave.getParticipants();
		//console.log("renderParticipants: participants.length=" + participants.length);
		var html = "";
		for(var pIdx = 0; pIdx < participants.length; pIdx++) {
			html += "<li> ";
			html += participants[pIdx].getDisplayName();
			html += " : ";

			var symbol = getParticipantSymbol(participants[pIdx]);
			//console.log("renderParticipant id=" + participants[pIdx].getId() + " symbol=" + symbol);
			if(symbol) {
				html += symbol;
			}
			else {
				html += "(observer)";
			}
		}
		participantInfo.innerHTML = html;
	}
}

function getCellState(cellId) {
	if (!wave.getState()) {
		return;
	}
	return wave.getState().get(cellId);
}

function renderGrid() {
	var grid = document.getElementById("nxGrid");
	if(grid) {
		var html = "";
		for (var row = 0; row < 3; row++) {
			html += "<tr>";
			for (var col = 0; col < 3; col++) {
				var cellId = "c"+row+col;
				var state = getCellState(cellId);
				html += "<td id='";
				html += cellId;
				html += "' class='nxTd'>";
				if(state) {
					html += state;
				}
				else {
					html += "<input type='radio' name='";
					html += cellId;
					html += "' onClick='play(this);'/>";
				}
				html += "</td>";
			}
			html += "</tr>";
		}
		grid.innerHTML = html;
	}
}

function play(radioButton) {
	if (!wave.getState()) {
		return;
	}
	var cellId = radioButton.name;
	//console.log("play: cellId=" + cellId);

	//only legal to pick the cell if it was null
	if(!getCellState(cellId)) {
		//console.log("p2");


		//only legal to play if the Viewer is 1st/2nd participant
		if(getViewerSymbol()) {
			//console.log("p3");

			var state = wave.getState();
			delta = {};
			delta[cellId] = getViewerSymbol();
			//console.log("play: delta=" + delta);
			state.submitDelta(delta);
		}
	}
}

//register the init() method
gadgets.util.registerOnLoadHandler(init);
</script>




<!-- and now the graphical parts -->

<style type="text/css">
	#nx {
		font-family:verdana,helvetica,arial,sans-serif;
		font-size: 0.8em;
	}
	#nxgrid {
		border-collapse:collapse;
	}
	#nxgrid td, td.nxTd {
		/**border:1px solid #C3C3C3;
		width: 12px;**/
		border:1px solid #CCCCCC;
		color:black;
		cursor:text;
		font-weight:bold;
		height:2em;
		line-height:2em;
		position:relative;
		text-align:center;
		width:2em;
	}
</style>

<div id="nx">
	<h2>Noughts &amp; Crosses (or Tic Tac Toe)</h2>
	<p>An multi-player game experiment by Andrew Liles July 2009. v0.1.15</p>

	<p>Participants:</p>
	<ul id="participantInfo"></ul>

	<p>To play click one of the radio buttons:</p>
	<table id="nxGrid">
	</table>

	<p>Limitations:</p>
	<ol>
		<li>Participants need to play nicely - ie. play in order of each other; there is no game logic to control this</li>
		<li>There is no game logic to deduce win/stalemate</li>
		<li>Once game is complete you need to start a new wave / re-load this gadget</li>
		<li>Only working in Firefox, not IE</li>
		<li>Participant leaving is not handled</li>
	</ol>
</div>


  ]]>
  </Content>
</Module>