(function () {
	function orderByThreshold(a,b) {
		return b.threshold - a.threshold;
	}
	function createFakeEvent(evtype,remote,diff,ind) {
		
		var evObj = document.createEvent('Events');
		evObj.initEvent( evtype, false, true );
		evObj.remoteData = remote;
		evObj.difference = diff;
		evObj.detail = ind;
		
		wiievents.dispatchEvent(evObj);
	}
	function createFakeKeyEvent(evtype,remote,keyCode,ind) {
		
		var evObj = document.createEvent('UIEvents');
		evObj.initUIEvent( evtype, false, false, window, ind );
		evObj.remoteData = remote;
		evObj.keyCode = keyCode;
		
		wiievents.dispatchEvent(evObj);
	}
	function pollForChanges() {
		var tempValues = {}, tempRemote, tempkeys, difference, absdifference, n, numlist;
		for( var i = 0; i < maxRemotes; i++ ) {
			if( remotesToCheck[i] && ( tempRemote = wiiremote.update(i) ).isEnabled ) {
				
				
				tempValues.roll = Math.atan2(tempRemote.dpdRollY,tempRemote.dpdRollX);
				tempValues.distance = tempRemote.dpdDistance;
				tempValues.vert = tempRemote.dpdY;
				tempValues.horiz = tempRemote.dpdX;
	
				
				if( evToCheck.rollEvents.numeric.length ) {
					difference = tempValues.roll - remotedatastore[i].roll;
					absdifference = Math.abs(difference);
					
					if( absdifference > Math.PI ) {
						absdifference = ( 2 * Math.PI ) - absdifference;
						difference = ( ( ( difference > 0 ) ? -2 : 2 ) * Math.PI ) + difference;
					}
					numlist = evToCheck.rollEvents.numeric;
					for( n = 0; n < numlist.length; n++ ) {
						
						if( absdifference >= numlist[n].threshold && !createFakeEvent(numlist[n].eventname,tempRemote,difference,i) ) {
							break;
						}
					}
				}
				
				if( evToCheck.distanceEvents.numeric.length ) {
					difference = tempValues.distance - remotedatastore[i].distance;
					absdifference = Math.abs(difference);
					numlist = evToCheck.distanceEvents.numeric;
					for( n = 0; n < numlist.length; n++ ) {
						if( absdifference >= numlist[n].threshold && !createFakeEvent(numlist[n].eventname,tempRemote,difference,i) ) {
							break;
						}
					}
				}
				if( evToCheck.movehorizEvents.numeric.length ) {
					difference = tempValues.horiz - remotedatastore[i].horiz;
					absdifference = Math.abs(difference);
					numlist = evToCheck.movehorizEvents.numeric;
					for( n = 0; n < numlist.length; n++ ) {
						if( absdifference >= numlist[n].threshold && !createFakeEvent(numlist[n].eventname,tempRemote,difference,i) ) {
							break;
						}
					}
				}
				if( evToCheck.movevertEvents.numeric.length ) {
					difference = tempValues.vert - remotedatastore[i].vert;
					absdifference = Math.abs(difference);
					numlist = evToCheck.movevertEvents.numeric;
					for( n = 0; n < numlist.length; n++ ) {
						if( absdifference >= numlist[n].threshold && !createFakeEvent(numlist[n].eventname,tempRemote,difference,i) ) {
							break;
						}
					}
				}
				if( evToCheck.moveEvents.numeric.length ) {
					difference = Math.sqrt( Math.pow( tempValues.horiz - remotedatastore[i].horiz, 2 ) + Math.pow( tempValues.vert - remotedatastore[i].vert, 2 ) );
					absdifference = Math.abs(difference);
					numlist = evToCheck.moveEvents.numeric;
					for( n = 0; n < numlist.length; n++ ) {
						if( absdifference >= numlist[n].threshold && !createFakeEvent(numlist[n].eventname,tempRemote,difference,i) ) {
							break;
						}
					}
				}
				
				remotedatastore[i].roll = tempValues.roll;
				remotedatastore[i].distance = tempValues.distance;
				remotedatastore[i].vert = tempValues.vert;
				remotedatastore[i].horiz = tempValues.horiz;
				
				for( n = 0; n < keyCodes.length; n++ ) {
					tempkeys = tempRemote.hold & keyCodes[n];
					if( tempkeys && !remotedatastore[i].keysdown[keyCodes[n]] ) {
						
						remotedatastore[i].keysdown[keyCodes[n]] = true;
						createFakeKeyEvent('keydown',tempRemote,keyCodes[n],i)
						createFakeKeyEvent('keypress',tempRemote,keyCodes[n],i)
					} else if( !tempkeys && remotedatastore[i].keysdown[keyCodes[n]] ) {
						
						remotedatastore[i].keysdown[keyCodes[n]] = false;
						createFakeKeyEvent('keyup',tempRemote,keyCodes[n],i)
					} else if( tempkeys ) {
						
						createFakeKeyEvent('keypress',tempRemote,keyCodes[n],i)
					}
				}
			}
		}
	}
	
	if( !window.opera || !opera.wiiremote ) { return; }
	var maxRemotes = 4; 
	
	var pollingInterval;
	var remotesToCheck = [];
	var remotedatastore = [];
	for( var i = 0; i < maxRemotes; i++ ) {
		remotedatastore[i] = { roll: 0, distance: 2.5, vert: -1, horiz: -1, keysdown: {} };
	}
	var evToCheck = { rollEvents: {numeric:[]}, distanceEvents: {numeric:[]}, moveEvents: {numeric:[]}, movevertEvents: {numeric:[]}, movehorizEvents: {numeric:[]} };
	var eventPattern = /wii(move(vert|horiz)?|roll|distance)-([\d\.]+)/;
	var keyCodes = [1,2,4,8,16,256,512,1024,2048,4096,8192,16384];
	var wiiremote = opera.wiiremote;
	
	var wiievents = opera.wiievents = document.createElement('wiievents');
	
	wiievents.realAddEventListener = wiievents.addEventListener;
	wiievents.realRemoveEventListener = wiievents.addEventListener;
	wiievents.addEventListener = function (eventtype,handler,phase) {
		var captures;
		if( typeof(eventtype) == 'string' && ( captures = eventtype.match(eventPattern) ) ) {
			
			var eventbase = evToCheck[captures[1]+'Events'], thresh = captures[3];
			if( !eventbase[eventtype] ) {
				eventbase.numeric[eventbase.numeric.length] = eventbase[eventtype] = { threshold: thresh * 1, eventname: eventtype, count: 0, currentindex: 0 };
			}
			eventbase[eventtype].count++;
			eventbase.numeric.sort(orderByThreshold);
			for( var i = 0; i < eventbase.numeric.length; i++ ) {
				eventbase.numeric[i].currentindex = i;
			}
		}
		wiievents.realAddEventListener(eventtype,handler,phase);
	};
	wiievents.removeEventListener = function (eventtype,handler,phase) {
		var captures;
		if( typeof(eventtype) == 'string' && ( captures = eventtype.match(eventPattern) ) ) {
			
			var eventbase = evToCheck[captures[1]+'Events'], thresh = captures[3];
			if( !eventbase[eventtype] ) { return; }
			eventbase[eventtype].count--;
			if( !eventbase[eventtype].count ) {
				
				eventbase.numeric.splice(eventbase[eventtype].currentindex,1);
				for( var i = eventbase[eventtype].currentindex; i < eventbase.numeric.length; i++ ) {
					eventbase.numeric[i].currentindex = i;
				}
				delete eventbase[eventtype];
			}
		}
		wiievents.realAddEventListener(eventtype,handler,phase);
	};
	
	wiievents.setPolling = function (interval) {
		if( pollingInterval ) { clearInterval(pollingInterval); }
		if( interval ) {
			pollForChanges();
			pollingInterval = setInterval(pollForChanges,interval);
		}
	};
	wiievents.monitorRemote = function (remoteNum,enable) {
		remotesToCheck[remoteNum] = enable;
	};
})();