var DOMAIN;
if( document.domain == 'yes.com' ) {
	// IE is so freaking stupid...
	document.location = 'http://www.yes.com/';
} else {
	DOMAIN = document.domain;
	document.domain = 'yes.com';
}

$extend( Function.prototype, {
	compose: function(binder, param1) {
		function call(param2) { return this.attempt([param1,param2],binder); }
		return call.bind(this);
	},
	arg1: function(param1) {
		function call(param2) { return this.attempt([param1,param2]); }
		return call.bind(this);
	},
	ifconnected: function(binder) {
		var func = this;
		var session = binder.session;
		return function(){ return (session == binder.session && binder.connected)? func.apply(binder,arguments): null }
	},
	ifdisconnected: function(binder) {
		var func = this;
		var session = binder.session;
		return function(){ return (session == binder.session && !binder.connected)? func.apply(binder,arguments): null }
	}
});

$extend( String.prototype, {
	asHTML: function(){
		var str = new String(this);
		str = str.replace( /&/g, '&amp;' );
		str = str.replace( /</g, '&lt;' );
		str = str.replace( />/g, '&gt;' );
		return str;
	},
	asPlain: function(){
		var str = new String(this);
		str = str.replace( /&amp;/g, '&' );
		str = str.replace( /&lt;/g, '<' );
		str = str.replace( /&quot;/g, '"' );
		str = str.replace( /&apos;/g, '\'' );
		return str.replace( /&gt;/g, '>' );
	},
	asAvatarURL: function(big){
		return '/user/avatar.php?user=' + encodeURIComponent(this) + '&big=' + (big? 1: 0);
	},
	asAmazonURL: function(){
		return 'http://www.amazon.com/gp/redirect.html?link_code=ur2&tag=cdstcd-20&camp=1789&creative=9325&location=/gp/search%3F%26index=music%26_encoding=UTF8%26keywords=' + encodeURIComponent(this);
	},
	asTime: function(){
		var parts = new String(this).replace(/^0/,'').split(':',3);
		if( parts[0] >= 12 ) {
			return ((parts[0]==12)? 12: (parts[0]-12)) +':'+parts[1]+'pm';
		} else {
			return ((parts[0]==0)? 12: parts[0]) + ':'+parts[1]+'am';
		}
	},
	asDate: function(){
		var parts = new String(this).split('/',3);
		return parts[1] + '.' + parts[0] + '.' + parts[2];
	}
});

$extend( Date.prototype, {
	getDateString: function(){
		var day = this.getDate().withLeadingZero();
		var mon = (this.getMonth()+1).withLeadingZero();
		var year = (this.getYear()-100).withLeadingZero();
		return (mon+'/'+day+'/'+year).asDate();
	},
	getTimeString: function(){
		var hour = this.getHours().withLeadingZero();
		var min = this.getMinutes().withLeadingZero();
		return (hour + ':' + min).asTime();
	},
	setYesterday: function(){
		this.setTime( this.getTime() - (1000 * (60 * 60 *24)) );
		return this;
	},
	getDayOfTheWeek: function(){
		var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
		return days[this.getDay()];
	},
	getMonthName: function(){
		var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
		return months[this.getMonth()];
	}
});

$extend( Number.prototype, {
	asiTunesURL: function(stream){
		return 'http://www.yes.com/itunes.php?mid=' + encodeURIComponent(this) + '&sid=' + encodeURIComponent(stream);
	},
	withLeadingZero: function(){
		// only adds a leading zero if it's less than 2 digits (using it for date/time conversion stuff)
		var s = '' + this;
		return (s.length<2)? ('0'+s): s;
	},
	asPlain: function(){
		return (''+this).asPlain();
	},
	asHTML: function(){
		return (''+this).asHTML();
	},
	asAmazonURL: function(){
		return (''+this).asAmazonURL();
	}
});

$extend( Events.prototype, {
	removeEvents: function(){
		this.$events = null;
	}
});

var Ajax = {
	get: function(url, params, func, fail) { Ajax._do(false,url,params,func,fail) },
	post: function(url, params, func, fail) { Ajax._do(true,url,params,func,fail) },
	_do: function(post, url, params, func, fail) {
		var x, p = '';
		try { x = new ActiveXObject('Msxml2.XMLHTTP') } catch(e){}
		if( !x ) try { x = new ActiveXObject('Microsoft.XMLHTTP') } catch(e){}
		if( !x ) try { x = new XMLHttpRequest() } catch(e){}
		if( params ) for( var k in params ) { if(p.length) p += '&'; p += encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) }
		x.open(post? 'POST': 'GET', 'http://' + DOMAIN + url + (post? '': ('?'+p)));
		if( post ) {
			x.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
			x.setRequestHeader( 'Connection', 'close' );
		}
		x.onreadystatechange = function() {
			if( (func || fail) && x.readyState == 4 ) {
				if( x.responseText ) {
					if(func) func( eval('('+x.responseText+')') );
				} else {
					if(fail) fail();
				}
				func = fail = null;
			}
		}
		x.send(post? p: null);
		setTimeout( function(){ if (fail) fail() }, 15000 );
	}
}

var Relay = {
	requests: 0,
	callbacks: {},
	queue: {},
	get: function(relay, url, onSuccess, onFailure) {
		if( !Relay.queue[relay] ) {
			Relay.queue[relay] = new Array();
			var iframe = new Element( 'iframe', {
				name: 'iframe' + Math.random(),
				id: 'iframe' + Math.random(),
				styles: {width: 0, height: 0, border: 0, padding: 0, margin: 0, position: 'absolute', top: '-100px'},
				src: 'http://' + relay + '.yes.com/ajax/iframe.html'
			});
			var error = setTimeout( function(){ 
				iframe.remove();
				delete Relay.queue[relay];
			}, 14000 );
			iframe.addEvent( 'load', function(){ $clear(error) });
			setTimeout( function(){
				iframe.injectInside( document.body );
			}, 1000 );
		}
		var id = Relay.requests++;
		var expire;
		function remove() { $clear(expire); delete Relay.callbacks[id]; }
		Relay.callbacks[id] = function(object) {
			remove();
			var func = (!object || object.type == 'error')? onFailure: onSuccess;
			if( func ) func.delay( 0, this, object );
		}
		expire = setTimeout( function(){ remove(); if (onFailure) onFailure() }, 15000 );
		Relay.queue[relay].push({ file:url, id:id });
	},
	got: function(object, id) {
		if( Relay.callbacks[id] ) (Relay.callbacks[id])( object );
	}
}

var Stream = new Class({
	initialize: function(relay,path) {
		this.fetch = function( service, onSuccess, onFailure ) { Relay.get(relay, path+service, onSuccess, onFailure) }
	},

	connect: function() {
		if( !this.connected ) {
			this.fireEvent( 'connecting' );
			this.connected = true;
			this.session = new Date().getTime();
			function booted(data) {
				this.fireEvent( 'connected', [data] );
				this.process( data );
				this.booted = true;
				this.fireEvent( 'booted' );
			}
			this.fetch( '/boot', booted.ifconnected(this), this.reconnect.ifconnected(this) );
		}
		return this;
	},

	disconnect: function() {
		this.connected = false;
		this.booted = false;
		this.fireEvent( 'disconnect' );
	},

	reconnect: function() {
		this.disconnect();
		this.connect();
	},

	pollurl: function(id) {
		return '/ev/' + id;
	},

	process: function(data) {
		$A(data.entries).each( this.fireEvent.compose(this,'entry') );
		this.next( data.next, 1000*((data.nextAt > 0)? data.nextAt: 2) );
	},

	next: function(id, interval) {
		function poll() { this.interrupt = function(){}; this.fetch( this.pollurl(id? id: 0), this.process.ifconnected(this), poll.ifconnected(this) ); }
		var timer = setTimeout( poll.ifconnected(this), interval );
		function interrupt() {
			$clear( timer );
			this.next( id, 1 );
		}
		this.interrupt = interrupt.bind( this );
	},

	interrupt: function() {}
});
Stream.implement(new Events);

var Channel = Stream.extend({
	initialize: function(id) {
		this._empty();
		function connected(data) { this.processRoster(data.roster) }
		this.addEvent( 'connected', connected.bind(this) );
		this.addEvent( 'entry', this.processEvent.bind(this) );
		this.parent( id.relay, '/relay/' + encodeURIComponent(id.stream) );
		$extend( this, id );
	},

	startRosterCount: function(){
		if( !this.rosterpoll ) {
			this.rosterpoll = true;
			this.backgroundrosterpoll();
		}
		return this;
	},

	stopRosterCount: function(){
		this.rosterpoll = false;
		return this;
	},

	leave: function(to) {
		// this does no good at all unless you stop polling the channel as usual...
		// also note that backgroundrosterpoll won't really do anything if you're not disconnected
		// so it's pretty safe
		function poll(){ this.backgroundrosterpoll() }
		var crumb = to? ('&id=' + encodeURIComponent(to.stream) + '&n=' + encodeURIComponent(to.name) + '&r=' + to.relay + '&t=' + encodeURIComponent(to.type)): '';
		this.fetch( '/relay?leave=1'+crumb, poll.bind(this), poll.bind(this) );
	},

	disconnect: function(to) {
		this._empty();
		this.parent();
		this.leave(to);
	},

	_empty: function() {
		this.queue = [];
		this.roster = [];
	},

	backgroundrosterpoll: function(d) {
		if( !this.rosterpoll ) return;
		if( $defined(d) ) {
			this.fireEvent( 'roster-count', [$pick(d.count,0)] );
		}
		function refresh() {
			this.fetch( '/count', this.backgroundrosterpoll.ifdisconnected(this), this.backgroundrosterpoll.ifdisconnected(this) );
		}
		setTimeout( refresh.ifdisconnected(this), $defined(d)? 10000: 500 );
	},

	send: function(msg) {
		if( msg && this.connected ) {
			this.queue.push( msg.substr(0,200) );	// with hard length limit
			this.interrupt();
		}
	},

	pollurl: function(id) {
		if( this.queue.length )
			return '/relay?msg=' + encodeURIComponent(this.queue.shift()) + '&ev=' + id;
		return this.parent(id);
	},

	next: function(id,interval) {
		return this.parent( id, this.queue.length? 1: interval );
	},

	processRoster: function(data) {
		this.roster = [];
		if( data && data.length ) for( var i=0; i<data.length; i++ ) this.roster.include(data[i].nick);
		this.fireEvent('roster-refresh',[this.roster]);
		this.fireEvent('roster-count',[this.roster.length]);
		function refresh() {
			this.fetch( '/roster', this.processRoster.ifconnected(this), this.processRoster.ifconnected(this) );
		}
		setTimeout( refresh.ifconnected(this), 2*60*1000 );
	},

	processEvent: function(event) {
		switch( event.type ) {
			case 'join':
				event.from = ''+event.from;
				this.roster.include( event.from );
				this.fireEvent('user-join',[event]);
				this.fireEvent('roster-refresh',[this.roster]);
				this.fireEvent('roster-count',[this.roster.length]);
				break;
			case 'leave':
				event.from = ''+event.from;
				this.roster.remove( event.from );
				this.fireEvent('user-leave',[event]);
				this.fireEvent('roster-refresh',[this.roster]);
				this.fireEvent('roster-count',[this.roster.length]);
				break;
			case 'chat':
				event.from = ''+event.from;
				this.fireEvent('user-message',[event]);
				break;
			case 'feed':
				event.title = ''+event.title;
				this.fireEvent('feed-item',[event]);
				break;
			case 'song':
				event.by = ''+event.by;
				event.title = ''+event.title;
				this.fireEvent('song-item',[event]);
				break;
			case 'advertisement':
				event.by = ''+event.by;
				event.title = ''+event.title;
				this.fireEvent('ad-item',[event]);
				break;
		}
	}
});

function loadChannelList( src, fn ) {
	function done( data ) {
		data.channels.each( function(c){
			fn( new Channel(c) );
		}, this);
	}
	Ajax.get( src, null, done.bind(this), loadChannelList.bind(this, [src,fn]) );
}

