/* --------------------------------------------------------------------

NET4VISIONS.COM SHOUTCAST - shoutcast.js 12-13-2009
Version: 1.0.2
Created by: net4visions.com
Last edited: 02-02-2010
Descriptions: retrieves recently played songs from shoutcast
and retrieves covers from Amazon using AJAX
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
Requirements: mootools core 1.2.4.js || mootools more 1.2.4.2.js
Compress online: http://www.refresh-sf.com/yui/ || http://yui.2clics.net/ 

-------------------------------------------------------------------- */

var Shoutcast = new Class({
	Implements: [Options, Events],
		
	options: {
		path:			'./assets/site/scripts/shoutcast.php',
		host:			'rs3.radiostreamer.com',
		port:			9330,
		limit:			15,		// >= 1 || <= 20
		covers:			2,		// <= limit
		loader:			true,	// show loader
		delay:			15000,	// milliseconds between request
		resume:			30000,	// milliseconds to wait before restart
		track:			null,
		trackIndex: 	null,
		filter:			'CRS Reminder|Werbung|CRS Commercial',
		lightbox:		false
		//onCallback:	$empty,
		//onUpdate:		$empty,
		//onStart:		$empty,
		//onPause:		$empty,
		//onStop:		$empty
	},
		
	
	initialize: function(el, options) {
		this.setOptions(options);
		this.slideContainerEl = document.id(el);
		if (!this.slideContainerEl) return false;

		this.timeoutID = null;
		this.trackTime = null;
		this.isUpdate  = this.options.track ? true : false;
		this.loaderEl  = document.id('loaderID');
		
		this.request();
	},

	
	request: function() { // get track information
		this.params = new Hash({
			'host': 	this.options.host,
		    'port': 	this.options.port,
		 	'limit':	this.options.limit,
		 	'covers':	this.options.covers,
		 	'title':	this.options.track
		});
		
		this.jsonRequest = new Request.JSON({
		    method:			'post',
		    url:			this.options.path,
		    initialDelay:	0,
		    delay:			this.options.delay,
		    limit:			90000,
		    noCache:		true,
		    link:			'chain',
		    onRequest:		function() {
		    	if (this.options.loader) this.loaderEl.show();
		    }.bind(this),
		    onSuccess: function(items) {
		    	// checks
				if (!this.itemsAreValid(items)) return false; // invalid response or request timed out
		    	if (!this.itemsNeedUpdate(items)) return false; // time has not changed - no updated needed
		    	// reset tables
		    	this.tblTracklist 		= null;
		    	this.tblTracklistExt	= null;
		    	
		    	// process items
		    	items.each(function(item, idx) {
		    		if (!$defined(item.track)) return false;
		    		this.processItems(item, idx);
		    	}.bind(this));
		    	
		    	this.fireEvent('onCallback')
		    }.bind(this),
		   	onComplete: function() {
		    	if (this.options.loader) this.loaderEl.hide();
		    }.bind(this)
		});
		
		this.options.track ? this.jsonRequest.post(this.params) : this.jsonRequest.startTimer(this.params);
	},
	
	
	itemsAreValid: function(items) { // check if valid ajax response
		if ($type(items) != 'array') return false; // invalid response
		if (items[0].track.test('Operation timed out')) return false; // operation timed out
		if (items[0].track.test('Connection timed out')) return false; // operation timed out
		
		return true;
	},
	
	
	itemsNeedUpdate: function(items) { // check if items need update
		if (this.options.track) return true; // single track request needs to be updated anyway
		
		if (items[0].time == this.trackTime) return false; // no update required
			
		// set new track time
		this.trackTime = $defined(items[0].time) ? items[0].time : null;
		return true;
	},
	
	
	processItems: function(item, idx) { // process track items	
		this.processSlides(item, idx);
		this.processTables(item, idx);
	},
	
	
	processSlides: function(item, idx) { // process slides
		var ignore = item.track.test(this.options.filter, 'i');
		if (this.isUpdate) {
			var itemEl = this.slideContainerEl;
		} else {
			var itemEl = this.slideContainerEl.getElements('.track')[idx];
			itemEl.eliminate('cover');
			itemEl.store('track', item.track);
			itemEl.empty();
			
			var html = this.setTrackHtml(item, ignore, false); 
			new Element('div', {
				'class': 	'descr',
				html:		html
			}).inject(itemEl);
			
			if (ignore) { // is CRS track => no ignores anymore onUpdate track
				itemEl.store('cover', false); // no need to retrieve on changing slides
				return false;
			}
		}
		 
		// max number of covers reached
		if (idx >= this.options.covers) return false;
		
		// add cover if available
		var coverEl = this.addCover(item, itemEl);
		itemEl.store('cover', coverEl);
		if (!coverEl) return false; // no cover found - no need to continue
		
		coverEl.inject(itemEl, 'top');
		
		var orderEl = this.addOrder(item);
		if (!orderEl) return false; // unable to add order link
		
		//orderEl.inject(itemEl);
		orderEl.inject(itemEl,'bottom');
	},
	
	
	processTables: function(item, idx) { // process tables
		var ignore = item.track.test(this.options.filter, 'i');
		
		if (this.isUpdate) {
			if ($defined(document.id('tracklistExtID'))) {
				var trEl 	= document.id('tracklistExtID').getElements('tr')[this.options.trackIndex+1];
				var tdEl 	= trEl.getElements('td')[2];
				var itemEl	= tdEl.getElement('.track');
			}
		} else {
			// tracklist :: tracklist regular excl. covers
			if (!this.tblTracklist) {
				var divEl = document.id('tracklistID');
				divEl.empty();
				
				var tblEl = this.prepareTable();
				tblEl.inject(divEl);
				this.tblTracklist = tblEl;
			}
			
			var html 	= this.setTrackHtml(item, ignore, true); 
			var itemEl	= new Element('div', { 
				'class' : 'track'
			}).adopt (
				new Element('div', {
					'class': 'descr',
					html: html
				})
			)
			
			var rObj = this.tblTracklist.push([idx+1, item.time, itemEl]);
			rObj.tr.store('slideIndex', idx); // need to navigate slides in player
			
			// tracklistExt :: tracklist extended incl. covers
			if ($defined(document.id('tracklistExtID'))) {
				if (!$defined(this.tblTracklistExt)) {
					var divEl = document.id('tracklistExtID');
					divEl.empty();
					var tblEl = this.prepareTable();
					tblEl.inject(divEl);
					this.tblTracklistExt = tblEl;
				}
				
				var html = this.setTrackHtml(item, ignore, true); 
				var itemEl = new Element('div', { 
					'class' : 'track'
				}).adopt (
					new Element('div', {
						'class': 'descr',
						html: html
					})
				)
				
				var rObj = this.tblTracklistExt.push([idx+1, item.time, itemEl]);
				var trEl = rObj.tr;
				trEl.store('slideIndex', idx);
				
				if (ignore) return false; // CRS track
				
				// add toggle links
				var aEl = this.addToggleLink(aEl);
				aEl.inject(rObj.tds[2], 'top');
				aEl.store('slideIndex', idx); // needed to retrieve cover
				
				// max number of covers reached
				if (idx >= this.options.covers) return false;
			}
		}
		
		
		// add cover if available
		if ($defined(document.id('tracklistExtID'))) {
			var coverEl = this.addCover(item);
			if (!coverEl) { // remove toggle link from row item
				var aEl = trEl.getElement('a.toggle-track');
				if ($defined(aEl)) {
					trEl.set('tween', {
						onComplete: function() {
							aEl.dispose();
						}
					}).highlight('#cc0000'); 
				};
				
				return false; // no cover found - no need to continue
			}
			
			coverEl.inject(itemEl);
			trEl.addClass('on');
			
			var orderEl = this.addOrder(item);
			if (!orderEl) return false; // unable to add order link
			
			orderEl.inject(itemEl);
		}
	},
	
	
	prepareTable: function() { // prepare empty tables and header
		// prepare table
		var tbl = new HtmlTable({
			properties: {
				border: 0,
				cellspacing: 0
			},
			zebra: true
		});
			
		tbl.setHeaders(['<!-- Number -->','Zeit','Titel']);
		return tbl;
	},	
	
	
	addToggleLink: function(el) { // add link to toggle item body
		var aEl = new Element('a', {
			href: 		'',
			title: 		'',
			'class':	'toggle-track' 
		});
		
		return aEl;	
	},
	
	
	addCover: function(item) { // add item body element
		if (!$defined(item.cover) || !$defined(item.cover.SmallImage)) return false; // no cover found		
		
		var divEl = new Element('div', {
			'class': 'cover'
		}).adopt(
			new Element ('img', {
				src: 		item.cover.SmallImage,
				height:		'75px',
				width: 		'75px',
				title:		'',
				alt:		''
			})
		);
		
		var aEl = this.addLightbox(item);
		if (aEl) aEl.wraps(divEl.getFirst());
		
		return divEl;
	},
	
	
	addLightbox: function(item) { // add lightbox link
		if (!this.options.lightbox) return false;
		// either no LargeImage or is CRS moderator
		if (!$defined(item.cover) || !$defined(item.cover.LargeImage)) return false;
		
		var c = item.cover.LargeImage.replace('%3A',':');	
		var aEl = new Element('a', { 
			href: 		c,
			rel: 		'lightbox[cover]',
			title: 		'',
			target:		'_blank'
		});
		
		return aEl;
	},
	
	
	addOrder: function(item) { // add cede.ch link
		
		// either no LargeImage or is CRS moderator
		if (!$defined(item.cover) || !$defined(item.cover.LargeImage)) return false;
		
		// unable to extract artist from whole track
		if (!item.track.contains(' - ')) return false;
		
		var parts 	= item.track.split(' - ', 2);
		
		var cedeUrl	= 'http://www.cede.ch/de/music-cd/partner.cfm';
		var params 	= new Hash({
			pid: 		'1180', // CEDE.ch ID
			Interpret:	parts[0].trim(),
			Track:		parts[1].trim()
		}).toQueryString();
		
		var divEl = new Element('div', {
			'class' : 'order'
		}).adopt(
			new Element('a', {
				href: 		cedeUrl +'?'+ params,
				text: 		'Bestellen...',
				title: 		'',
				rel: 		'external',
				target:		'_blank',
				'class':	'tooltip'
			})
		);
		
		return divEl;
	},
	
	
	setTrackHtml: function(item, crs, table) { // set html for track - artist, title, checks for url and email address
		var table = table || false;
		var delim = table ? ' - ' : '<br />';
		
		var parts = item.track.split(' - ', 2);
		parts[0]  = parts[0].replace('on CRS -', '-'); // search for moderator identifier
		var html  = '<span class="title">' + item.track + '</span>';
		if (parts.length > 1) {
			var html = '';
			if (!table) {
				html += '<span class="time">'   + item.time + '</span>' + delim;
			}
			html += '<span class="artist">' + parts[0].trim() + '</span>' + delim;
			html += '<span class="title">'  + parts[1].trim() + '</span>';
		}
		
		if (!crs) return html; // not CRS track - no further action needed
		
		// is CRS track
		html = '<span class="title"><em>' + html + '</em></span>';	
	
		// look for url in track
		var re = /((?:http|https):\/\/[a-z0-9\/\?=_#&%~-]+(\.[a-z0-9\/\?=_#&%~-]+)+)|(www(\.[a-z0-9\/\?=_#&%~-]+){2,})/gi
		var match = item.track.match(re);
		if (match) { // add http:// if not there
			match = (match[0].match(/^www\./i)) ? 'http://' + match[0] : match[0];
			return html.replace(re,'<a title="" href="'+match+'" rel="external">'+match.toLowerCase()+'</a>');
		}
		
		// look for email in track
		var re = /[a-zA-Z0-9._-]+@[a-zA-Z0-9]+.[a-z]+(.[a-z]+)?/gi;
		var match = item.track.match(re);
		if (match) {
			return html.replace(re, '<a title="" rel="external" href="mailto:'+match[0]+'">'+match[0].toLowerCase()+'</a>');
		}
		
		return html;
	},
	
	
	start: function() { // start json request
		this.timeoutID = this.jsonRequest.startTimer(this.params);
		this.fireEvent('onStart');
	},
	
	
	pause: function() { // pause json request
		if (this.timeoutID) $clear(this.timeoutID);
		
		this.jsonRequest.stopTimer();
		this.timeoutID = this.start.delay(this.options.resume, this);
		this.fireEvent('onPause');
	},
	
	
	stop: function() { // stop json request
		if (this.timeoutID) $clear(this.timeoutID);
		
		this.jsonRequest.stopTimer();
		this.fireEvent('onStop');
	},
	
	
	reset: function(el) { // reset json request
		this.trackTime = false;
		this.slideContainerEl = document.id(el);
		this.start();
	}
});
