function htmlEncode(text) {
   var div = document.createElement('div');
   div.appendChild(document.createTextNode(text));
   return div.innerHTML;
}

function getSoundHTML(url, mimetype) {
	var html = '<object border="0" width="0" height="0">\n'
			+ '\t<param name="src" value="' + htmlEncode(url) + '" />\n'
			+ '\t<param name="code" value="' + htmlEncode(url) + '" />\n'
			+ '\t<param name="autoplay" value="true" />\n'
			+ '\t<param name="controller" value="false" />\n'
			+ '\t<param name="type" value="' + htmlEncode(mimetype) + '" />\n'
			+ '\t<embed src="' + htmlEncode(url) + '" code="' + htmlEncode(url) + '" controller="false" autoplay="true" type="' + htmlEncode(mimetype) + '" width="0" height="0" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" pluginspace="http://www.apple.com/quicktime/download/" />\n'
			+ '</object>\n';
	return html;
}

function isOpera() {
	return navigator.userAgent.toLowerCase().indexOf('opera') == -1 ? false : true;
}

function isMSIE() {
	return navigator.userAgent.toLowerCase().indexOf('msie') == -1 ? false : true;
}

function isMSIELessThan7() {
	return isMSIE() && !window.XMLHttpRequest;
}

function setBGSound(url) {
	var bgsoundElement;
	var bgsoundElements = document.getElementsByTagName('bgsound');
	if (bgsoundElements.length == 0) {
		bgsoundElement = document.createElement('bgsound');
		var bodyElements = document.getElementsByTagName('body');
		bodyElements[0].appendChild(bgsoundElement);
	} else {
		bgsoundElement = bgsoundElements[0];
	}
	bgsoundElement.src = url;
}

/* 
Quite an ugly hack but this is unfortunately needed because FireFox on WinXP (with no quicktime installed)
needs a mimetype of application/x-mplayer2 in the object/embed tags in order to actually play a midi file
All other user agents seem to do fine with a mimetype of audio/mid. The idea here is that we traverse the navigator.plugins
object to figure out what mimetypes are supported and then choose one of the supported types. But since browser support
is sketchy for the navigator.plugins collection (and related properties), we carefully check to see if each property is
defined before we try to access it.
*/
function getMidiMimeType() {
	if (typeof(navigator) != 'undefined' && typeof(navigator.plugins) != 'undefined' && typeof(navigator.plugins.length) != 'undefined') {
		for (var i=0; i<navigator.plugins.length; i++) {
			var plugin = navigator.plugins[i];

			if (typeof(plugin.length) != 'undefined') {
				for (var j=0; j<plugin.length; j++) {
					var item = plugin[j];

					var mimeTypeSupportsMidi = false;
					var wildcardSuffix = false;
					var midiSuffix = false;

					if (typeof(item.type) != 'undefined') {
						mimeTypeSupportsMidi = item.type.search(/^(?:audio\/(?:x-)?midi?|application\/(?:x-)?mplayer\d*)$/) != -1 ? true : false;
					}

					if (typeof(item.suffixes) != 'undefined') {
						var suffixes = item.suffixes.split(',');
						for (var k=0; k<suffixes.length; k++) {
							if (suffixes[k].search(/^midi?$/) != -1) {
								midiSuffix = true;
							} else if (suffixes[k] == '*') {
								wildcardSuffix = true;
							}
						}
					}
					if (midiSuffix || mimeTypeSupportsMidi && wildcardSuffix) {
						return item.type;
					}
				}
			}
		}
	}
	return 'audio/mid';
}

function playMidi(url) {
	// MSIE doesn't handle urlencoded # or + symbols properly
	/*
	if (isMSIE()) {
		url = url.replace(/#/g, '%23');
		url = url.replace(/\+/g, '%2B');
	}
	*/
	if (isMSIELessThan7()) {
		// msie6 needs special treatment because it needs to use the bgsound tag to play sounds
		setBGSound(url);
	} else {
		var soundContainer = document.getElementById('soundContainer');
		if (soundContainer == null) {
			soundContainer = document.createElement('div');
			soundContainer.id = 'soundContainer';
			var bodyElements = document.getElementsByTagName('body');
			bodyElements[0].appendChild(soundContainer);
		} else {
			// remove children
			while (soundContainer.hasChildNodes()) {
				// enclose in try catch because if 2 threads are competing to remove children, the loser may try to remove a child that is not there
				try { soundContainer.removeChild(soundContainer.firstChild); } catch (e) {}
			}
		}
		soundContainer.innerHTML = getSoundHTML(url, getMidiMimeType());
	}
}

// stupidly, escape() doesn't encode the following characters: * @ - _ + . /
// newer browers support encodeURI() and encodeURIComponent() but for maximum backward compatibility, we roll our own.
// more info here: http://xkr.us/articles/javascript/encode-compare/
function urlEncode(input) {
	var output = escape(input);
	output = output.replace('@', '%40');
	output = output.replace('+', '%2B');
	output = output.replace('/', '%2F');
	return output;
}

/*
function searchEngineFriendly2QueryString(searchEngineFriendly) {
	var schemeAndDomain = searchEngineFriendly.replace(/^([a-zA-Z0-9]+:\/\/[^\/]+).*$/, '$1');	
	var trimmed = searchEngineFriendly.replace(/^[a-zA-Z0-9]+:\/\/[^\/]+\/?(.*)$/, '$1');	
	var pathParts = trimmed.split('/');
	var keys;
	var path = schemeAndDomain + '/' + pathParts[1] + '.' + pathParts[0];
	switch (pathParts[1]) {
		case 'chordshape':
			keys = Array('root', 'chord', 'bass', 'define', 'a', 'd', 'instrument', 'tuning', 'capo', 'fretSpan');
			break;
		case 'chord':
			keys = Array('root', 'chord');
			break;
		case 'scale':
			keys = Array('root', 'scale');
			break;
		default:
			keys = null;
	}
	
	var querystring = '';
	// skip the first 2 parts of the path because they are 'mid' and 'scale'|'chord'|'chordshape'
	for (var i=2; i<pathParts.length; i++) {
		// if this is the last element it might have a file extension on it that we need to discard
		if (i == pathParts.length - 1) {
			// strip the file extension while maintaining any querystring present
			pathParts[i] = pathParts[i].replace(/^(.*)\.[a-zA-Z0-9]+\??(.*)$/, '$1&$2');
			// strip a trailing '&' if we ended up with one in the previous step
			pathParts[i] = pathParts[i].replace(/^(.*)&$/, '$1');
		}
		querystring += (i == 2 ? '?' : '&') + keys[i - 2] + '=' + pathParts[i]; 
	}
	return path + querystring;
}
*/

function getMidiUrl(define, instrument, tuning, chord, root, bass, capo, fretSpan, arpeggioOffset) {
	return getQueryStringMidiUrl(define, instrument, tuning, chord, root, bass, capo, fretSpan, arpeggioOffset);
}

function getSearchEngineFriendlyMidiUrl(define, instrument, tuning, chord, root, bass, capo, fretSpan, arpeggioOffset) {
	var defaultPathComponents = new Array('none', 'custom', root, null, '1', 'down', 'Guitar', 'Standard', '0', '4');
	var values = new Array(root, chord, bass, define, arpeggioOffset, 'down', instrument, tuning, capo, fretSpan);
	var pathComponents = new Array();
	var includeValue = false;
	for (var i=defaultPathComponents.length-1; i>=0; i--) {
		var value = values[i];
		if (value != null) {
			pathComponents[i] = value;
			includeValue = true;
		} else {
			if (includeValue) {
				pathComponents[i] = defaultPathComponents[i];
			}
		}
	}
	var schemeAndDomain = document.location.toString().replace(/^([a-zA-Z0-9]+:\/\/[^\/]+).*$/, '$1');
	var searchEngineFriendlyUrl = schemeAndDomain + '/mid/chordshape';
	for (var i=0; i<pathComponents.length; i++) {
		searchEngineFriendlyUrl += '/'  + urlEncode(pathComponents[i]);
	}
	searchEngineFriendlyUrl += '.mid';
	
	return searchEngineFriendlyUrl;
}

function getQueryStringMidiUrl(define, instrument, tuning, chord, root, bass, capo, fretSpan, arpeggioOffset) {
	var url = '/chordshape.mid';
	url += '?define=' + urlEncode(define);
	if (instrument != null) {
		url += '&instrument=' + urlEncode(instrument);
	}
	if (tuning != null) {
		url += '&tuning=' + urlEncode(tuning);
	}
	if (chord != null) {
		url += '&chord=' + urlEncode(chord);
	}
	if (root != null) {
		url += '&root=' + urlEncode(root);
	}
	if (bass != null) {
		url += '&bass=' + urlEncode(bass);
	}
	if (capo != null) {
		url += '&capo=' + urlEncode(capo);
	}
	if (fretSpan != null) {
		url += '&fretSpan=' + urlEncode(fretSpan);
	}
	if (arpeggioOffset != null) {
		url += '&a=' + urlEncode(arpeggioOffset);
	}
	return url;
}
