var puzzle = (function () {
	var top = 40, left = 40,
		margin = 60,
		width = 900, height = 675,
		pieces = [], neighbor_ref,
		filename,
		master = null,
		order, number, edge,
		win_width, win_height,
		solution_timer, solution_closing = false,
		js_ready, flash_ready;

	var VIDEO = 1, PICTURE = 2,
		puzzle_type;

	// if any one window closes, close all windows and pause master video (if necessary)
	var closeWindows = function () {
		if (!solution_closing) {
			if (puzzle_type == VIDEO && master && master.pauseVideo) {
				master.pauseVideo();
			}

			for (var i = 0, len = pieces.length; i < len; ++i) {
				pieces[i].window.close();
				pieces[i].video = null;
				pieces[i].window = null;
			}
			
			pieces = [];
			neighbor_ref = [];
			master = null;
		} else {
			--number;
			if (number == 1) {
				solution_closing = false;
			}
		}
	}

	// open puzzle piece windows
	var openWindows = function () {
		var temp, original;

		closeWindows();

		js_ready = 0;
		flash_ready = 0;
		order = [];

		// figure out what kind of puzzle this is from the clicked link
		config = this.href.replace(/%23/g, '#');
		config = config.split('#');

		puzzle_type = config[1] == "video" ? VIDEO : PICTURE;
		number = parseInt(config[2]);
		filename = config[3];
		edge = Math.sqrt(number);
		win_width = width / edge;
		win_height = height / edge;

		// generate a random ordering for the windows
		temp = number - 1;
		while (temp >= 0) order.push(temp--);
		original = order.concat();
		while (original.toString() == order.toString()) {
			order.sort( function(a,b) { return Math.round(Math.random()) ? 1 : -1; } );
		}

		// open puzzle piece windows
		for (var i = 0; i < number; ++i) {
			window.open('child.html', 'window' + i, 'location=no,status=no,menubar=no,toolbar=no,scrollbars=no,status=no,width=' + win_width + ',height=' + win_height + ',left=' + left + ',top=' + top);
		}

		return false;
	}

	// offsets puzzle video/image based on this piece's ordering in the puzzle
	var setOffset = function (piece) {
		var row = Math.floor(piece.order / edge),
			col = piece.order % edge,
			x_ratio = -win_width,
			y_ratio = -win_height;

		piece.video.style.top = (row * y_ratio) + 'px';
		piece.video.style.left = (col * x_ratio) + 'px';
	}

	// returns object defining pieces neighboring this one, when pieces are in correct order
	var getNeighbors = function (piece) {
		return {
			top: (piece - edge >= 0) ? piece - edge : null,
			bottom: (piece + edge < number ) ? piece + edge : null,
			left: ( Math.floor((piece - 1) / edge) == Math.floor(piece / edge)) ? piece - 1 : null,
			right: ( Math.floor((piece + 1) / edge) == Math.floor(piece / edge)) ? piece + 1 : null
		}
	}

	var checkSolution = function () {
		var win_ref,
			neighbor,
			solution = true,
			row = 0, col = 0;

		// get updated window coordinates
		for (var i = 0, len = pieces.length; i < len; i++) {
			win_ref = pieces[i].window;
			pieces[i].x = win_ref.screenLeft || win_ref.screenX;
			pieces[i].y = win_ref.screenTop || win_ref.screenY;
		}

		// now check each piece for the solution; stop if, at any point, something fails the test
		for (i = 0, len = pieces.length; i < len && solution; i++) {
			if (pieces[i].neighbors.top !== null) {
				neighbor = neighbor_ref[pieces[i].neighbors.top];
				if ( (pieces[i].x < neighbor.x - margin || pieces[i].x > neighbor.x + margin) ||
					 (pieces[i].y - win_height < neighbor.y - margin) || (pieces[i].y - win_height > neighbor.y + margin) ) {
					solution = false;
				}
			}
			if (pieces[i].neighbors.bottom !== null && solution) {
				neighbor = neighbor_ref[pieces[i].neighbors.bottom];
				if ( (pieces[i].x < neighbor.x - margin || pieces[i].x > neighbor.x + margin) ||
					 (pieces[i].y + win_height < neighbor.y - margin) || (pieces[i].y + win_height > neighbor.y + margin) ) {
					solution = false;
				}
			}
			if (pieces[i].neighbors.left !== null && solution) {
				neighbor = neighbor_ref[pieces[i].neighbors.left];
				if ( (pieces[i].x - win_width < neighbor.x - margin || pieces[i].x - win_width > neighbor.x + margin) ||
					 (pieces[i].y < neighbor.y - margin) || (pieces[i].y > neighbor.y + margin) ) {
					solution = false;
				}
			}
			if (pieces[i].neighbors.right !== null && solution) {
				neighbor = neighbor_ref[pieces[i].neighbors.right];
				if ( (pieces[i].x + win_width < neighbor.x - margin || pieces[i].x + win_width > neighbor.x + margin) ||
					 (pieces[i].y < neighbor.y - margin) || (pieces[i].y > neighbor.y + margin) ) {
					solution = false;
				}
			}
		}

		if (solution) {
			clearInterval(solution_timer);
			
			setTimeout(function () {
				var first = neighbor_ref[0].window;

				solution_closing = true;
				master = neighbor_ref[0].video;

				// close all windows except first
				for (var i = neighbor_ref.length - 1; i > 0; --i) {
					neighbor_ref[i].window.close();
				}

				// resize first window and restart video
				first.resizeTo(width, height);
				if (puzzle_type == VIDEO) {
					master.turnOnSound();
				}

			}, 1500);
		}
	}

	// sync video frames (may not be used);
	var syncVideo = function () {
		var frame = master.getTime();
		for (var i = 1; i < number; i++) {
			pieces[i].video.updateTime(frame);
		}
	}

	var startPuzzle = function () {
		// kick off solution timer and video playback in all players (if this is a video)
		setTimeout(function () {
			if (puzzle_type == VIDEO) {
				master = pieces[0].video;
				master.turnOnSound();
				master.playVideo();
				for (i = 1, len = pieces.length; i < len; ++i) pieces[i].video.playVideo();
				//sync_timer = setInterval(syncVideo, 5000);
			}

			solution_timer = setInterval(checkSolution, 500);
		}, 1000);
	}

	return {
		init: function () {
			// capture link clicks
			document.getElementById('video4').onclick = openWindows;
			document.getElementById('picture16').onclick = openWindows;
			document.getElementById('picture25').onclick = openWindows;

			// kill all window pieces when parent is unloaded
			window.onunload = closeWindows;
		},

		// called when a piece is fully loaded
		ready: function (js) {
			if (js || puzzle_type == PICTURE) {
				js_ready++;
			} else {
				flash_ready++;
			}

			// if everyone has registered, let's roll
			if ((puzzle_type == PICTURE && js_ready == number) || (puzzle_type == VIDEO && js_ready == number && flash_ready == number)) {
				startPuzzle();
			}
		},

		// called by opened windows to register with the puzzle code
		registerPiece: function (piece) {
			var row, col,
				index, number, neighbors,
				img;

			// initialize video player
			if (puzzle_type == VIDEO) {
				piece.window.swfobject.createSWF(
					{ data: filename, width: width, height: height },
					{ wmode: 'transparent', allowscriptaccess: 'always' },
					'video'
				);
			}

			// add piece to collection
			number = order.pop();
			neighbors = getNeighbors(number);
			index = pieces.length;
			pieces[index] = {
				window: piece,
				order: number,
				neighbors: neighbors,
				video: piece.document.getElementById('video')
			};
			neighbor_ref[number] = pieces[index];

			if (puzzle_type == PICTURE) {
				img = document.createElement('img');
				img.src = filename;
				img.width = '' + width;
				img.height = '' + height;
				pieces[index].video.appendChild(img);
			}

			pieces[index].window.onunload = closeWindows;

			setTimeout(function () {
				setOffset(pieces[index]);

				// move window into position
				row = index % edge;
				col = Math.floor(index / edge);
				piece.moveTo(left + (row * win_width), top + (col * win_height));
				
				// tell the controller that this window is ready
				puzzle.ready(true);
			}, 1000);
		}
	}
})();