var AdRotator = Class.create({
	version: '1.0',
	
	setOptions: function( options ) {
		this.options = {
			delay: 12,
			duration: 0.4,
			filePath: "/_includes/ads/",
			filePathDynamic: "/_includes/ad.cfm",
			
			rows: 1,
			rowheight: "160px",
			rowsize: 360,
			rowClassName: "promo_row",
			slotClassName: "promo_slot"
		}
		Object.extend( this.options, options || {} );
	},
	
	initialize: function( container, ads, options ) {
		this.setOptions(options);
		
		this.container = $(container);
		
		this.ads = $A(ads);
		this.ads.each( function(obj) { obj.count = 0; });
		
		// add rows to main container
		this.rows = [];
		for ( r = 0; r < this.options.rows; r++ )
			this.container.insert( this.rows[r] = new Element( "div", { className: this.options.rowClassName }).setStyle({ height: this.options.rowheight }) );
		
		// create visible slot for each row, then fill
		this.adsshown = [];
		for ( r = 0; r < this.options.rows; r++ ) {
			var slot = new Element( "div", { className: this.options.slotClassName });
			this.rows[r].insert(slot);
			this.fillSlot(slot);
		}
		
		// create hidden slot for each row, then fill
		this.adsshown = [];
		for ( r = 0; r < this.options.rows; r++ ) {
			var slot = new Element( "div", { className: this.options.slotClassName }).hide();
			this.rows[r].insert(slot);
			this.fillSlot(slot);
		}
		
		this.animate.bind(this).delay(this.options.delay);
	},
	
	fillSlot: function(slot) {
		slot.update("");
		
		var s = this.options.rowsize, c, i;
		while ( s > 0 ) {
			c = -1;
			
			if ( this.adsshown.length == this.ads.length )
				this.adsshown = [];
			
			// gather candidates that fit and have been shown the fewest times
			var adstoshow = this.ads.inject( [], function( ret, ad, indx ) {
				// may be in middle of row when all ads have been shown; prevent ads shown in this row from appearing again
				// ...or ad is too large for the row
				if ( this.adsshown.indexOf(indx) != -1 || ad.size > s )
					return ret;
				
				if ( c == -1 || ad.count < c ) {
					// no ads have been added, or this ad has been shown fewer times
					c = ad.count;
					return [indx];
				}
				else if ( ad.count == c ) {
					// include this ad among candidates
					ret.push(indx);
				}
				
				return ret;
			}.bind(this) );
			
			if ( adstoshow.length == 0 )  break;
			
			i = adstoshow[ Math.floor( Math.random() * adstoshow.length ) ];
			this.ads[i].count++;
			
			this.adsshown.push(i);
			s -= this.ads[i].size + 40;
			
			slot.insert( this.getAd(this.ads[i]).addClassName( s > 0 ? "col" : "rcol" ) );
		}
	},
	
	getAd: function(ad) {
		var div = new Element("div");
		
		if ( Object.isUndefined(ad.cache) && Object.isUndefined(ad.file) ) {
			new Ajax.Updater( div, this.options.filePathDynamic + "?AdID=" + ad.id, {
				onComplete: function(transport) { ad.cache = transport.responseText; },
				onException: function(transport,err) { alert(err); }
			});
			return div;
		}
		else if ( Object.isUndefined(ad.cache) ) {
			new Ajax.Updater( div, this.options.filePath + ad.file, {
				onComplete: function(transport) { ad.cache = transport.responseText; },
				onException: function(transport,err) { alert(err); }
			});
			return div;
		}
		else
			return div.update(ad.cache);
	},
	
	animate: function() {
		this.adsshown = [];
		
		for ( r = 0; r < this.options.rows; r++ ) {
			this.rows[r].childElements().each( function(slot) {
				if ( slot.visible() )
					slot.fade({ duration: this.options.duration, afterFinish: this.fillSlot.bind(this,slot) });
				else
					slot.appear({ duration: this.options.duration });
			}.bind(this) );
		}
		
		this.animate.bind(this).delay(this.options.delay);
	}

});
