function saveStats(stats) {}

function loadQsParams() {
	var params = getQs();
	for (var i in params) {
		settings[i] = params[i];
		if (typeof(gbid(i).checked)!="undefined") {
			gbid(i).checked = (params[i] == "false" || params[i] == "off" || params[i] == "no" || params[i]=="0" || !params[i]) ? false : true;
		}
		else {
			gbid(i).value = params[i];
		}
		if (gbid(i).onchange) {
			gbid(i).onchange();
		}
	}
}

function getQs() {
	var params = {};
	var argSpecs = window.location.search.substring(1).split('&');
	if (window.location.search) {
		for (var i in argSpecs) {
			var argInfo = argSpecs[i].split('=');
			params[unescape(argInfo[0])] = unescape(argInfo[1]);
		}
	}
	return params;
}


var overload = {
	equal: {},
	notEqual: {},
	copy: {},
	addReplace: {},
	isIn: {}
};
function equal(a, b) { return overload.equal[typeof(a)](a, b); }
function notEqual(a, b) { return overload.notEqual[typeof(a)](a, b); }
function copy(a) { return overload.copy[typeof(a)](a); }
function addReplace(a, b) { return overload.addReplace[typeof(a)](a, b); }
function isIn(a, b) { return overload.isIn[typeof(b)](a, b); }

overload.equal.array = function(arr1, arr2) {
	if (arr1.length!=arr2.length) { return false; }
	for (var i=0; i<arr1.length && i<arr2.length; i++) {
		if (notEqual(arr1[i],arr2[i])) {
			return false;
		}
	}
	return true;
}
overload.notEqual.array = function(arr1, arr2) {
	if (arr1.length!=arr2.length) { return true; }
	for (var i=0; i<arr1.length && i<arr2.length; i++) {
		if (notEqual(arr1[i],arr2[i])) {
			return true;
		}
	}
	return false;
}
overload.copy.array = function(arr) {
	var newArr = [];
	for (var i=0; i<arr.length; i++) {
		newArr[i] = copy(arr[i]);
	}
	return newArr;
}
overload.addReplace.array = function(arr1, arr2) {
	for (var i=0; i<arr1.length; i++) {
		arr1[i] += arr2[i];
	}
	return arr1;
}
overload.isIn.array = function(el, arr) {
	for (var i=0; i<arr.length; i++) {
		if (equal(arr[i], el)) {
			return true;
		}
	}
	return false;
}

overload.equal.object = function(arr1, arr2) {
	if (arr1.length) { return overload.equal.array(arr1, arr2); }
	for (var i in arr1) {
		/// NYI
	}
}
overload.notEqual.object = function(arr1, arr2) {
	if (arr1.length) { return overload.notEqual.array(arr1, arr2); }
	for (var i in arr1) {
		/// NYI
	}
}
overload.copy.object = function(ob) {
	if (ob.length) { return overload.copy.array(ob); }
	var newOb = {};
	for (var i in ob) {
		newOb[i] = copy(ob[i]);
	}
	return newOb;
}
overload.addReplace.object = function(arr1, arr2) {
	if (arr1.length) { return overload.addReplace.array(arr1, arr2); }
	for (var i in arr2) {
		if (typeof(arr1[i])=="undefined") {
			arr1[i] = arr2[i];
		}
	}
}
overload.isIn.object = function(el, ob) {
	if (ob.length) { return overload.isIn.array(el, ob); }
	for (var i in ob) {
		if (ob[i]==el) {
			return true;
		}
	}
	return false;
}

overload.equal.number = function(a, b) {
	return a === b;
}
overload.notEqual.number = function(a, b) {
	return a !== b;
}
overload.copy.number = function(a) {
	return a;
}

overload.equal.string = function(a, b) {
	return a === b;
}
overload.notEqual.string = function(a, b) {
	return a !== b;
}
overload.copy.string = function(a) {
	return a;
}

/** Quickly remove an element from an array that's used as a set **/
function deleteFromSet(arr, el) {
	for (var i=0; i<arr.length; i++) {
		if (equal(arr[i], el)) {
			arr[i] = arr[arr.length-1];
			arr.length--;
			return true;
		}
	}
	return false;
}

/// Create a list of numbers over [min,max] in steps of 'step'
function createListFromRange(min, max, step) {
	if (!step) { step = 1; }
	var num = Math.ceil((1 + max - min)/step);
	console.log(num);
	var arr = new Array(num);
	for (var i=0; i<num; i++) {
		arr[i] = min + step*i;
	}
	return arr;
}

/// Remove a random element from the array 'arr', return it, and readjust the array
function removeRandom(arr) {
	var i = Math.floor(Math.random()*arr.length);
	val = arr[i];
	arr[i] = arr[arr.length-1];
	arr.length--;
	return val;
}

function setScriptOnload(scriptEl, onload) {
	///Nice and simple for Moz/Opera
	scriptEl.onload = function() { onload(scriptEl); };

	/*@cc_on @*/
	/*@if (@_win32)
			window.setTimeout(function checkScriptLoad(){
				if (scriptEl.readyState == "loaded") {
					headLoc.removeChild(scriptEl);
					if (onload) { onload(scriptEl); }
				}
				else {
					window.setTimeout(checkScriptLoad, 30);
				}
			}, 30);
	/*@end @*/
}

/// Adapted from http://www.theurer.cc/blog/2005/12/15/web-services-json-dump-your-proxy/
function requestJson(url, callback) {
	noCacheIE = '&noCacheIE=' + (new Date()).getTime();
	headLoc = document.getElementsByTagName("head").item(0);
	scriptId = requestJson.scriptCounter ? requestJson.scriptCounter++ : 1;
  var scriptObj = document.createElement("script");
  if (callback) { setScriptOnload(scriptObj, callback); }

  // Add script object attributes
  scriptObj.setAttribute("type", "text/javascript");
  scriptObj.setAttribute("src", url + noCacheIE);
  scriptObj.setAttribute("id", "requestJson-"+scriptId);
  headLoc.appendChild(scriptObj);
}

/// These are the javascript versions
var requests = {
	getWords: "jseval:getWords.js?",
	getRandomTextExtract: "jseval:getRandomTextExtract.js?"
};
function setServerSettings() {
	with (requests) {
		getWords = "getWords.php?";
		getRandomTextExtract = "getRandomTextExtract.php?";
	}
}

function getWords(callback, options) {
	if (!options) { options = {}; }
	if (!options.freq) { options.freq = null; }
	if (!options.limit) { options.limit = null; }
	if (!options.numWords) { options.numWords = 10; }
	if (!options.minWordLength) { options.minWordLength = 0; }
	if (!options.wordLength) { options.wordLength = "any"; }
	if (!options.fileCheck) { options.fileCheck = ""; }
	if (!options.duplicate) { options.duplicate = false; }
	if (!options.regexp) { options.regexp = ""; }
	var wordLength = "";
	if (options.wordLength!="any") {
		wordLength = "&wordLength="+options.wordLength;
	}
	getLivePage(requests.getWords+"numWords="+options.numWords+"&duplicate="+options.duplicate+"&regexp="+options.regexp+"&minWordLength="+options.minWordLength+wordLength+"&fileCheck="+escape(options.fileCheck)+(options.freq?"&freq="+escape(options.freq):"")+(options.limit?"&limit="+escape(options.limit):""), function(httpr) {
		if (httpr.readyState==4) {
			//console.log("x", httpr.responseText);
			callback(httpr.responseText.split(","));
		}
	});
}

function getRandomTextExtract(callback) {
	getLivePage(requests.getRandomTextExtract, function(httpr) {
		if (httpr.readyState==4) {
			callback(httpr.responseText);
		}
	});
}

var mplayer = "qtime";
function loadSound(url) {
	if (mplayer == "qtime") {
		if (document.all) {
			var soundEl = newEl('<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" \
						codebase="http://www.apple.com/qtactivex/qtplugin.cab" \
						width="0" height="0" enablejavascript="true"> \
						<param name="src" value="'+url+'"> \
					<param name="autoplay" value="false"> \
					<param name="controller" value="false"> \
					<param name="cache" value="true"> \
			</OBJECT>');
		}
		else {
			var soundEl = newEl('<object data="'+url+'" type="video/quicktime" height="0" width="0" enablejavascript="true"> \
				<param name="autoplay" value="false"> \
				<param name="controller" value="false"> \
				<param name="cache" value="true"> \
				<param name="saveembedtags" value="true"> \
				</object>');
		}
		console.log(url, soundEl);
		document.body.appendChild(soundEl);
		soundEl.blur();
		return {
			soundEl: soundEl,
			destroy: function() {
				this.stop();
				document.body.removeChild(this.soundEl);
				this.soundEl = null;
			},
			stop: function() {
				if (typeof(this.soundEl.Stop)!="undefined") { this.soundEl.Stop(); }
			},
			load: function() {
				if (typeof(this.soundEl.Play)!="undefined") {
					this.soundEl.Play();
					console.log("play");
					var _this = this;
					window.setTimeout(function(){_this.soundEl.Stop();}, 50);
					console.log("pause");
				}
				else {
					var _this = this;
					window.setTimeout(function(){_this.load();}, 20);
				}
			},
			/// Focus bug (in Fx at least) requires elToFocus after playing (or rewinding, dunno which)
			play: function(elToFocus) {
				if (typeof(this.soundEl.Play)!="undefined") {
					console.log("play", this.soundEl);
					//alert('f');
					this.soundEl.Rewind();
					this.soundEl.Play();
					if (elToFocus) { elToFocus.blur(); elToFocus.focus(); }
				}
				else {
					console.log("yo", this.soundEl);
					var _this = this;
					window.setTimeout(function(){_this.play(elToFocus);}, 20);
				}
			},
			ENDSOUND: 0
		}
	}
	else if (mplayer=="wmp") {
		var soundEl = newEl('<object id="wmp_p" data="'+url+'" type="application/x-mplayer2" width="0" height="0" > \
				<param name="autostart" value="false" /> \
				<param name="volume" value="-100" /> \
				<p>Error - the plugin has not loaded</p> \
			</object>');
		document.body.appendChild(soundEl);
		return {
			soundEl: soundEl,
			stop: function() {
				if (this.soundEl.controls) { this.soundEl.controls.stop(); }
			},
			load: function() {
				if (this.soundEl.controls) {
					this.soundEl.controls.play();
					console.log("play");
					var _this = this;
					window.setTimeout(function(){_this.soundEl.controls.stop();}, 50);
					console.log("pause");
				}
				else {
					var _this = this;
					window.setTimeout(function(){_this.load();}, 20);
				}
			},
			play: function() {
				console.log(this.soundEl.controls);
				if (this.soundEl.controls) {
					console.log("play", this.soundEl.controls);
					this.soundEl.controls.stop();
					this.soundEl.controls.play();
				}
				else {
					console.log("yo", this.soundEl.controls);
					var _this = this;
					window.setTimeout(function(){_this.play();}, 20);
				}
			},
			ENDSOUND: 0
		}
	}
}

function getWikiWord(word, callback, args) {
	getLivePage("wiktionary_pronounce.php?word="+word, function(httpr) { if (httpr.readyState==4) {
		console.log(httpr.responseText);
		if (httpr.responseText) {
			callback.apply(null, [loadSound(httpr.responseText)].concat(args));
		}
		else {
			callback(null);
		}
	}});
}


function grabSettingsFromDoc(settings) {
	var changes = {};
	for (var setting in settings) {
		if (gbid(setting)) {
			var val = typeof(gbid(setting).checked)!="undefined" ? gbid(setting).checked : gbid(setting).value;
			if (settings[setting] != val) {
				changes[setting] = settings[setting];
			}
			settings[setting] = val;
		}
	}
	return changes;
}

function initSettings(settings, fixes) {
	var prevValues = {};
	if (fixes) {
		for (var setting in fixes) {
			prevValues[setting] = settings[setting];
		}
	}
	var changes = grabSettingsFromDoc(settings);
	if (fixes) {
		for (var setting in fixes) {
			eval('settings[setting] = '+fixes[setting].replace(new RegExp("\\b"+setting+"\\b","g"), "settings[setting]"));
			if (!equal(settings[setting],prevValues[setting])) {
				changes[setting] = prevValues[setting];
			}
			else if (changes[setting]) {
				delete changes[setting];
			}
		}
	}
	return changes;
}

function newEl(tag, innerHTML, props) {
	/// If tag looks like html, treat like html fragment with just 1 element
	if (tag.search(/</)!=-1) {
		var div = document.createElement("div");
		div.innerHTML = tag.replace(/^[^<]*/, ''); /// Get rid of anything at the start that''s not tag
		return div.firstChild;
	}
	/// Otherwise, treat as tag name
	else {
		var el = document.createElement(tag);
		for (var prop in props) {
			if (prop=="className") { el.className = props.className; }
			else {
				el.setAttribute(prop, props[prop]);
			}
		}
		el.innerHTML = innerHTML;
		return el;
	}
}

function newDiv(content, props) { return newEl("div", content, props); }

function mixin(ob, object2) {
	for (var i in object2) {
		ob[i] = object2[i];
	}
	if (ob._init) {
		ob._init();
	}
}

function setOpacity(el, opacity) {
	el.style.opacity = opacity;
	if (opacity==1) { el.style.filter = ""; }
	else { el.style.filter = "alpha(opacity="+Math.round(100*opacity)+")"; }
}

function updateRunningStats(fields) {
	gbid("runningStats").style.display = "block";
	function createBar(barHeight, barLeft, doAlt, className) {
		var barEl = newEl('<div class="graphBar'+(className ? " "+className : "")+(doAlt?' alt':'')+'"></div>');
		barEl.style.height = Math.round(barHeight)+'px';
		barEl.style.left = barLeft+"px";
		return barEl;
	}

	var start = allStats.length - 50;
	start = start < 0 ? 0 : start;
	var fieldStats = copy(fields);
	for (var field in fields) {
		fieldStats[field] = new RunningStats();
	}
	for (var i=start; i<allStats.length; i++) {
		for (var field in fields) {
			fieldStats[field].add(allStats[i][field]);
		}
	}
	var graphs = copy(fields);
	for (var field in fields) {
		graphs[field] = newEl('<div class="runningGraph"></div>');
	}
	var barWidth = getIndependentClassProperty("graphBar", "offsetWidth");
	var barMaxHeight = getIndependentClassProperty("runningGraph", "offsetHeight");
	for (var i=start; i<allStats.length; i++) {
		for (var field in fields) {
			graphs[field].appendChild(
				createBar((barMaxHeight*allStats[i][field]/fieldStats[field].max), ((i-start)*barWidth), i%2)
			);
		}
		if (allStats[i].settingsChanged) {
			for (var field in fields) {
				graphs[field].appendChild(
					createBar(barMaxHeight, ((i-start)*barWidth), null, 'barSeparator')
				);
			}
		}
	}
	var num = allStats.length-start;
	addCssRule(".runningGraph", "width: "+((num<5?5:num)*barWidth)+"px");
	for (var field in fields) {
		gbid("running_"+field).innerHTML = "";
		gbid("running_"+field).appendChild(graphs[field]);
	}
	//gbid("runningMissesAvg").innerHTML = toPercent(mpmpStats.calcMean(), 0, true);
	//gbid("runningTimesAvg").innerHTML = Math.round(timeStats.calcMean())/1000+"s";
}