// tzot 2011-11-28

/*	NB: AirPlayNext.xml should provide at least one song at all times,
	and at least two songs whenever it falsely identifies the current
	song as the next coming up */

// custom namespace to avoid clashes
rg1o= {};
// now playing: an optional element with id rg1_streaminfo_now_elem
rg1o.info_now_elem= document.getElementById('rg1_streaminfo_now_elem');
// coming up: an optional element with id rg1_streaminfo_next_elem
rg1o.info_next_elem= document.getElementById('rg1_streaminfo_next_elem');
// debugging element; typically not required
rg1o.status_elem= document.getElementById('rg1_log_elem');
// override IE caching of XML requests using a counter and a session ID
rg1o.counter= 0
rg1o.sessionid= false
// parse time from a datetime as string
rg1o.parser= /\d{2}:\d{2}:\d{2}/
rg1o.songinfo= ''

// convert HH:MM:SS to msvalue
function time_to_ms(time_as_text) {
	return (
		time_as_text.substr(0, 2)*3600000
		+time_as_text.substr(3, 2)*60000
		+time_as_text.substr(6, 2)*1000);
}

// debugging log, only if debugging element exists
function rg1_logger(text) {
	if (rg1o.status_elem)
		rg1o.status_elem.innerHTML+= text + "<BR>";
}

// retrieve artistname - songname from XML node
function rg1_songinfo(node) {
	songname= node.attributes.getNamedItem('title').value;
	artistname= node.getElementsByTagName('Artist')[0].attributes.getNamedItem('name').value;
	return artistname + ' - ' + songname;
}

rg1_logger('in the script');

// return XMLHttpRequest object
function rg1_get_xmlhttp() {
	if (window.XMLHttpRequest) {
		try {
			result= new XMLHttpRequest();
		} catch (e) {
			result= false;
		}
	} else {
		try {
			result= new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e) {
			result= false;
		}
	}
	return result;
}

// callback for 'now playing' XML request
function rg1_xml_now_ready() {
	var xml, song_node, songinfo, expire_time, now, wait_ms, e;

    if (rg1o.xmlhttp.readyState == 4) {
        xml= rg1o.xmlhttp.responseXML;
		song_node= xml.getElementsByTagName('Song')[0];
		songinfo= rg1_songinfo(song_node);
		rg1_logger('songinfo: ' + songinfo);
		if (songinfo.substr(0, 3) != ' - ') {
			rg1o.info_now_elem.innerHTML= songinfo;
			rg1o.songinfo= songinfo;
		}

		// what does the server consider as now?
		now= rg1o.parser.exec(rg1o.xmlhttp.getResponseHeader('Date'));
		now= time_to_ms(now.toString());
		// use real now
		now= new Date();
		now= 3600000*now.getHours() + 60000*now.getMinutes() + 1000*now.getSeconds();
		rg1_logger('my now is ' + now);

		// calculate timeout delay
		if (true) { // use false to ignore time info and use constant timeout
			try {
				expire_time= song_node.getElementsByTagName('Expire')[0];
				expire_time= expire_time.attributes.getNamedItem('Time').value;
				rg1_logger('expire time is ' + expire_time + ' ' + time_to_ms(expire_time));
				expire_time= time_to_ms(expire_time) + 25000;

				/* WARNING: next line has a dirty trick */
				wait_ms= (expire_time - now) % 1800000;
				/* EXPLANATION:
				Server time is UTC; expire time is assumably EET/EEST but can't be sure,
				and can't be bothered to do timezone offset calculations.
				So: ignore all multiples of half-hours.
				Half-hours because some timezones are not round hours +/- UTC */

				if (wait_ms < 0) { // negative modulo, damn javascript
					wait_ms= 1800000 + wait_ms;
				}
				if (wait_ms > 900000 || wait_ms < 17000) { // stale XML, so try again soon
					wait_ms= 17000;
				}
			} catch (e) {
				rg1_logger('expire time exception: ' + e.description);
				wait_ms= 30000;
			}
		} else { // FIXED timeout delay
			wait_ms= 17000;
		}
		rg1_logger('ms sleeping: ' + wait_ms);
		setTimeout(rg1_update_status, wait_ms);
    }
}

// callback for 'coming up' XML request
function rg1_xml_next_ready() {
	var xml, songinfo;

	if (rg1o.xmlhttp_next.readyState == 4) {
		xml= rg1o.xmlhttp_next.responseXML;
		songinfo= rg1_songinfo(xml.getElementsByTagName('Song')[0]);
		rg1_logger('nextinfo: ' + songinfo);
		if (songinfo == rg1o.songinfo) { // the XML files are not in sync
			// so use the second upcoming song
			songinfo= rg1_songinfo(xml.getElementsByTagName('Song')[1]);
		}
		rg1o.info_next_elem.innerHTML= songinfo;
	}
}

// refresh song titles
function rg1_update_status() {
	var now, timenow, e;
	// create if needed xmlhttp object for 'now playing'
	if (rg1o.info_now_elem && ! rg1o.xmlhttp) {
		rg1o.xmlhttp= rg1_get_xmlhttp();
		rg1o.xmlhttp.onreadystatechange= rg1_xml_now_ready;
	}
	// create if needed xmlhttp object for 'coming up'
	if (rg1o.info_next_elem && ! rg1o.xmlhttp_next) {
		rg1o.xmlhttp_next= rg1_get_xmlhttp();
		rg1o.xmlhttp_next.onreadystatechange= rg1_xml_next_ready;
	}
	// calculate a dummy session id
	if (! rg1o.sessionid) {
		now= new Date()
		timenow= now.getHours()*3600000 + now.getMinutes()*60000 + now.getSeconds()*1000 + now.getMilliseconds();
		rg1o.sessionid= timenow.toString(36)
	}

	// issue a 'now playing' request
	if (! rg1o.xmlhttp) {
		rg1o.info_now_elem.innerHTML= "Great music!";
	} else {
		rg1_logger('getting NowOnAir.xml');
		try { rg1o.xmlhttp.open("GET", "/NowOnAir.xml?" + rg1o.sessionid + rg1o.counter, true);
		} catch (e) {
			rg1o.info_now_elem.innerHTML= 'The greatest music of all times!';
		}
		rg1o.counter++;
	}
	rg1o.xmlhttp.send();

	// issue a 'coming up' request
	if (! rg1o.xmlhttp_next) {
		rg1o.info_next_elem.innerHTML= 'More great music!';
	} else {
		rg1_logger('getting AirPlayNext.xml');
		try { rg1o.xmlhttp_next.open('GET', '/AirPlayNext.xml?' + rg1o.sessionid + rg1o.counter, true);
		} catch (e) {
			rg1o.info_next_elem.innerHTML= 'More great music of all times!';
		}
		rg1o.counter++;
	}
	rg1o.xmlhttp_next.send();
}

rg1_update_status();

