var AudioPlayer = new Class({
	
	Implements: [Options, Events],
	
	options: {
		
		events: {},
		debug: false,
		mode: false,						// flash or html
		prioritize: false,
		
		/* HTML5 player options */
		html5: {},
		
		/* Flash player options */
		flash: {
			swf_address: 'swf/AudioPlayer.swf'
		}
	},
	
	bubble_events: ['play', 'pause', 'ended', 'progress', 'playhead'],
	
	id: false,
	isLoaded: false,
	tracks: [],
	current_track: null,
	playing: false,
	pause: false,
	
	initialize: function(options)
	{		
		this.setOptions(options);
		
		// Get a global ID for the instance
		this.id = AudioPlayer.register(this);
		
		// Internal events	
		this.addEvents({
			'ready': function(){
				this.isLoaded = true;
			},
			'play': function(){
				this.playing = true;
				this.paused = false;
			}.bind(this),
			'pause': function(){
				this.playing = false;
				this.paused = true;
			}.bind(this)
		});
		
		// Custom events		
		this.addEvents(this.options.events);
		
		Object.merge(this.options.flash, {
			'events': {
				'load': function(){
					this.fireEvent('ready');
				}.bind(this),
				'onembed': function(){
					if( ! this.flash_player.active)
					{
						this.flash_player = false;
						this.debug('A current version of flash could not be found, or cannot be used on this device');
						this.fireEvent('ready');
					}
					else
					{
						this.bubble_events.each(function(event){
							this.flash_player.addEvent(event, function(){ this.fireEvent(event, arguments); }.bind(this));
						}.bind(this));
					}
				}.bind(this)
			}
		});	
		
		this.flash_player = new AudioPlayer.Flash(this.options.flash)
		
		
	},
	
	
	// --------------------------------------------------------------------
		
	/**
	 * Add a track to the playlist. Parameter should be an object including
	 * at least the property "url". The playlist parameter is not currently
	 * used.
	 * 
	 */
	add_track: function(track, playlist)
	{
		if(typeOf(track) == 'string') track = {url: track};
		this.tracks.push(track);
		
		this.debug('Track '+track.url+' added');
		return this.tracks.length - 1;
	},
	
	
	
	// --------------------------------------------------------------------
		
	/**
	 * Start Playback
	 * 
	 */
	play: function(track)
	{
		if(typeOf(track) == 'null' && this.player && this.player.paused)
		{
			// Resuming playback
		}
		else if(typeOf(track) == 'null')
		{
			// Grab the first track
			if(typeOf(this.tracks[0]))
			{
				track = 0;
			}
			else
			{
				this.debug('No tracks to play. Use add_track to add tracks, or pass a track to the play method');
				return false;
			}
		}
		
		if(typeOf(track) == 'string')
		{
			return this.play(this.add_track(track));
		}
		else if(typeOf(track) == 'number')
		{
			if(typeOf(this.tracks[track]) == 'null')
			{
				this.debug('Can\'t find track '+track);
				return false;
			}
			
			this.current_track = track;
			track = this.tracks[track];
			
			// Try HTML5
			this.player = new AudioPlayer.HTML5(track.url);
			
			var use = 'fallback';
			
			if(this.options.mode == 'flash' || (this.flash_player && this.options.prioritize == 'flash'))
			{
				use = 'flash';
			}
			else if(this.options.mode == 'html' || (this.player.canplay && this.options.prioritize == 'html'))
			{
				use = 'html';
			}
			
			if(use == 'flash' || (use == 'fallback' && ! this.player.canplay))
			{
				if(this.flash_player)
				{
					this.debug('HTML5 failed for '+track.url+'. falling back to flash player');
					this.player = this.flash_player;
					this.player.set_source(track.url);
				}
				else
				{
					this.debug('The file '+track.url+' could not be played using the HTML5 player & flash could not be used');
				}
			}
			else
			{
				this.bubble_events.each(function(event){
					this.player.addEvent(event, function(){ this.fireEvent(event, arguments); }.bind(this));
				}.bind(this));
				this.debug('Playing '+track.url+' using HTML5!');
			}
		}
		
		this.player.play();
		
		return true;
	},
	
	
	
	// --------------------------------------------------------------------
		
	/**
	 * Pause Playback
	 * 
	 */
	pause: function()
	{
		this.debug('pause');
		if(this.player) this.player.pause();
	},
	
	
	toggle: function()
	{
		if(this.player.paused) this.player.play();
		else this.player.pause();
	},
	
	
	// --------------------------------------------------------------------
		
	/**
	 * Prev
	 * 
	 */
	prev: function()
	{
		this.play(this.current_track - 1);
	},
	
	// --------------------------------------------------------------------
		
	/**
	 * Next
	 * 
	 */
	next: function()
	{
		this.play(this.current_track + 1);
	},
	
	// --------------------------------------------------------------------
		
	/**
	 * Seek - by percentage
	 * 
	 */
	seek: function(step)
	{
		if(this.player) this.player.seek(step);
	},
	
	
	// --------------------------------------------------------------------
		
	/**
	 * Debug
	 * 
	 */
	debug: function(message)
	{
		if(typeOf(trace) == 'function' && this.options.debug) trace('AudioPlayer '+this.id+': '+message);
	}
	

});

AudioPlayer.instances = [];
AudioPlayer.register = function(instance){
	return AudioPlayer.instances.push(instance);
};
AudioPlayer.stopAll = function(){
	AudioPlayer.instances.each(function(i){ i.pause(); });
};



// --------------------------------------------------------------------
	
/**
 * AudioPlayer.Engine
 * 
 */
AudioPlayer.Engine = new Class({
	
	Implements: [Options, Events],
	
	options: {
		events: {
			'play': function(){},
			'pause': function(){},
			'ended': function(){},
			'progress': function(){},	// Loading progress as a percentage 0-1
			'playhead': function(){}	// Playhead progress as a percentage 0-1
		}
	},
	
	get_mime_type: function(url)
	{
		var mime_type = 'unknown';
		switch(url.substr(url.lastIndexOf('.')+1))
		{
			case 'mp3':
				mime_type = 'audio/mpeg';
				break;
			case 'ogg':
				mime_type = 'audio/ogg';
				break;
			case 'wav':
				mime_type = 'audio/x-wav';
				break;
			case 'm4a':
				mime_type = 'audio/mp4a-latm';
				break;
		}
		
		return mime_type;
	}
	
});


// --------------------------------------------------------------------
	
/**
 * AudioPlayer.HTML5
 * 
 */
AudioPlayer.HTML5 = new Class({

	Extends: AudioPlayer.Engine,
	
	initialize: function(url)
	{
		// Only want one of these at a time!
		if($('audioplayer-html5')) $('audioplayer-html5').destroy();
		
		this.url = url;
		
		this.audio = new Element('audio', {'id': 'audioplayer-html5'});
				
		if(!!this.audio.canPlayType && this.canPlayFile())
		{
			this.canplay = true;
			
			new Element('source', {'src': this.url, 'type': this.mime_type}).inject(this.audio);
			this.audio.inject($(document.body));			
			this.audio.addEventListener('progress', function(){
				this.fireEvent('progress', this.audio.buffered.end(0) / this.audio.duration);
			}.bind(this));
			this.audio.addEventListener('timeupdate', function(){
				this.fireEvent('playhead', this.audio.currentTime / this.audio.duration);
			}.bind(this));
			this.audio.addEventListener('play', function(){
				this.playing = true;
				this.paused = false;
				this.fireEvent('play');
			}.bind(this));
			this.audio.addEventListener('pause', function(){
				this.playing = false;
				this.paused = true;
				this.fireEvent('pause');
			}.bind(this));
			this.audio.addEventListener('ended', function(){
				this.playing = false;
				this.paused = false;
				this.fireEvent('ended');
			}.bind(this));
		}
		else
		{
			this.canplay = false;
		}
	},
	
	canPlayFile: function(url)
	{
		if(typeOf(url) == 'null') url = this.url;
		
		this.mime_type = this.get_mime_type(url);
				
		return !!this.audio.canPlayType(this.mime_type);
	},
	
	
	play: function()
	{
		this.audio.play();
	},
	
	pause: function()
	{
		this.audio.pause();
	},
	
	
	/*
	*	Seek by percentage
	*/
	seek: function(i)
	{
		this.audio.currentTime = this.audio.duration * i;
	}
	
});





// --------------------------------------------------------------------
	
/**
 * AudioPlayer.Flash
 * 
 */
AudioPlayer.Flash = new Class({

	Extends: AudioPlayer.Engine,
	
	options: {
		swf_address: 'swf/AudioPlayer.swf',
		swf_events: {
			alert: 'alert',
			onLoad: 'onLoad',
			onPlay: 'onPlay',
			onPause: 'onPause',
			onProgress: 'onProgress',	/* Turned off for performance */
			onPlayhead: 'onPlayhead',	/* Turned off for performance */
			onComplete: 'onComplete'
		},
		events: {}
	},
	
	initialize: function(options)
	{
		this.setOptions(options);
		
		// Get a global ID for the instance
		this.id = AudioPlayer.Flash.register(this);
		this.active = true;
		
		this.addEvents(this.options.events);
		
		this.addEvents({
			'load': function(){
				// Reference for javascript interaction with the player
				this.player = (navigator.appName.indexOf("Microsoft") != -1) ? window['AudioPlayer'+this.id] : document['AudioPlayer'+this.id];
			}.bind(this),
			'play': function(){
				this.playing = true;
				this.paused = false;
			}.bind(this),
			'pause': function(){
				this.playing = false;
				this.paused = true;
			}.bind(this),
			'complete': function(){
				this.fireEvent('ended');
			}.bind(this)
		});
		
		// Create the container for the SWF object
		this.el = new Element('div', {'id': 'AudioPlayer-container-'+this.id, 'styles': {'position': 'absolute', 'bottom': 0, 'left': 0, 'display': 'block', 'opacity': 1, 'visibility': 'visible', 'width': 1, 'height': 1, 'overflow': 'hidden'}}).inject($(document.body));
		this.flash_container = new Element('div', {'id': 'AudioPlayer-swf-'+this.id}).inject(this.el);
		
		// Embed the swf
		var vars = {
			'player_id': this.id,
			'cb_handler': 'AudioPlayer.Flash.callback',
			'event_alert': this.options.swf_events.alert,
			'event_onLoad': this.options.swf_events.onLoad,
			'event_onPlay': this.options.swf_events.onPlay,
			'event_onPause': this.options.swf_events.onPause,
			'event_onProgress': this.options.swf_events.onProgress,
			'event_onPlayhead': this.options.swf_events.onPlayhead,
			'event_onComplete': this.options.swf_events.onComplete
		};
		var params = {wmode: 'transparent', allowScriptAccess: 'always'};
		var attributes = {id: 'AudioPlayer'+this.id, name: 'AudioPlayer'+this.id};
		swfobject.embedSWF(this.options.swf_address, 'AudioPlayer-swf-'+this.id, "100", "50", "9.0.0", '', vars, params, attributes, this.onembed.bind(this));
		
	},
	
	
	onembed: function(callback)
	{
		this.active = callback.success;
		this.fireEvent('onembed');
	},
	
	
	set_source: function(url)
	{
		this.player.set_source(url);
	},
	
	// --------------------------------------------------------------------
		
	/**
	 * Flash callback function
	 * 
	 */
	callback: function(event, data)
	{
		this.fireEvent(event, data);
	},
	
	
	play: function()
	{
		this.player.perform();
	},
	
	pause: function()
	{
		this.player.halt();
	},
	
	/*
	*	Seek by percentage
	*/
	seek: function(i)
	{
		this.player.seek(i);
	}
	
});

AudioPlayer.Flash.instances = [];
AudioPlayer.Flash.register = function(instance){
	return AudioPlayer.Flash.instances.push(instance);
};
AudioPlayer.Flash.callback = function(key, event, data){
	if(event == 'alert') alert(data);
	return AudioPlayer.Flash.instances[key.toInt()-1].callback(event, data);
};







// --------------------------------------------------------------------
	
/**
 * AudioPlayer.Osmek
 * 
 */

AudioPlayer.Osmek = new Class({
	
	Extends: AudioPlayer,
	
	options: {
		url: '', // URL of osmek JSON object
		post_data: {} // Data to post to the request URL
	},
	
	initialize: function()
	{
		this.parent.apply(this, arguments);
		
		new Request.JSON({url: this.options.url, method: 'post'}).addEvent('success', function(rsp){
			rsp.items.each(function(item){
				item.url = 'http://files.osmek.com/get/'+item.file;
				this.add_track(item);
			}.bind(this));
			this.fireEvent('ready');
		}.bind(this)).send(this.options.post_data);
	}
});





// --------------------------------------------------------------------
	
/**
 * AudioPlayer.Display
 * 
 */
 
AudioPlayer.Display = new Class({
	
	Extends: AudioPlayer,
	
	options: {
		track_class: '.track',
		handle_class: '.handle',
		button_class: '.button',
		progress_class: '.progress',
		handle_offset: 0
	},
	
	initialize: function(el, track, options)
	{
		this.setOptions(options);
		
		this.el = $(el);
		this.el.store('ClassRef', this);
		this.track = track;
		this.track_width = this.el.getElement(this.options.track_class).getSize().x;
		this.dragging = false;
		
		this.addEvents({
			'ready': function(){
				this.add_track(this.track);
				this.el.getElement(this.options.handle_class).addEvent('mousedown', function(){
					 this.dragging = true;
				}.bind(this));
				this.el.getElement(this.options.handle_class).addEvent('mouseup', function(){
					 this.dragging = false;
				}.bind(this));
				var mySlider = new Slider(this.el.getElement(this.options.track_class), this.el.getElement(this.options.handle_class), {
				    snap: true,
				    onTick: function(pos){
				        this.el.getElement(this.options.handle_class).setStyle('left', pos);
				    }.bind(this),
				    onComplete: function(step){
				    	this.dragging = false;
				    	var seek = step * .01;
				    	var drag_position = step * .01 * this.track_width;
				    	var progress_position = this.el.getElement(this.options.progress_class).getStyle('width').toInt() * this.track_width / 100;
				    	if(drag_position > progress_position)
				    	{
				    		seek = 1;
				    	}

				    	this.seek(seek);
				    }.bind(this)
				});
			}.bind(this),
			'play': function(){
				this.el.getElement(this.options.button_class).addClass('play').removeClass('pause');
			}.bind(this),
			'pause': function(){
				this.el.getElement(this.options.button_class).addClass('pause').removeClass('play');
			}.bind(this),
			'progress': function(percentage){
				this.el.getElement(this.options.progress_class).setStyle('width', percentage*100+'%');
			}.bind(this),
			'playhead': function(percentage){
				if( ! this.dragging)
				{
					this.el.getElement(this.options.handle_class).setStyle('width', percentage*this.track_width);
				}	
			}.bind(this)	

		});
		
		this.el.getElement(this.options.button_class).addEvent('click', function(e){
			e.stop();
			if( ! e.target.hasClass('play'))
			{
				AudioPlayer.stopAll();
				this.play();
			}
			else
			{
				this.pause();
			}
		}.bind(this));
		
		
		this.parent(options);
	},
	
	
	toElement: function()
	{
		return this.el;
	}
	
});
