// 
//  jQuery JW Player
// 	Created in order to excellently implement JW Player
// 	(http://www.longtailplayer.com/players/jw-flv-player/)
// 	with ease, yet still keeping it fully customizable.
// 	It's main purpose is to be a simple implementation for
// 	players and audio players alike.
//  
//  @author: Justin Jones (justin@jstnjns.com) http://www.jstnjns.com/
//  @version: 1.0
//  @copyright: 2010 Justin Jones. All rights reserved.
//  @requires: jQuery v1.4.x (http://www.jquery.com/)
//  @requires: Modernizr (http://www.modernizr.com/)
// 	@optional: SWFObject (http://code.google.com/p/swfobject/)
// 	@optional: jQuery UI - Slider (http://jqueryui.com/demos/slider/)
//

(function($) {
	
	$.fn.player = function(file, options) {
		
		return $(this).each(function() {
			
			var $this = $(this),
				unID,
				player,
				settings = {},
				status = {
					state		: 'IDLE',
					position	: 0,
					duration	: 0,
					current		: -1,
					previous	: -1,
					volume		: 0,
					mute		: false,
					fullscreen	: false
				},
				$container,
				$current,
				$image,
				$title,
				$controls,
				$position,
				$duration,
				$play,
				$stop,
				$prev,
				$next,
				$seek,
				$volume_group,
				$volume,
				$mute,
				$popout,
				$playlist,
				play,
				pause,
				stop,
				setSeek,
				setSeekBar,
				setVolume,
				setVolumeBar,
				mute,
				fullscreen,
				load,

				_init = function() {
					
					// Define settings from defaults and custom options
					$.extend(true, settings, $.fn.player.defaults, options);
					
					if($.isFunction(settings.callbacks.before)) { settings.callbacks.before.call(); }
					
					// Create the Unique global ID that will be used to separate global
					// functions for multiple instantiation.
					_createUniqueGlobal();
					
					if(settings.width == 'auto') { settings.width = $this.width(); }
					if(settings.height == 'auto') { settings.height = $this.height(); }
					if(settings.width < settings.min_width) { settings.width = settings.min_width; }
					if(settings.height < settings.min_height) { settings.height = settings.min_height; }
					
					settings.name = $this.attr('id');
					
					if(settings.mode == 'auto') {
						if(Modernizr.video && Modernizr.audio) {
							settings.mode = 'html';
						} else if(navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]) {
							settings.mode = 'flash';
						} else {
							settings.mode = 'default';
						}
					}
					
					switch(settings.mode) {
						case 'html':
							_buildHTML();
							break;
						
						case 'flash':
							_buildFlash();
							break;
							
						case 'default':
							_buildDefault();
							break;
					}
					
					if(settings.controls) { _buildControls(); }
				},
				
				_createUniqueGlobal = function() {
					var i = 0;
					while (true) {
						unID = 'player_' + i;
						if (!window[unID]) {
							window[unID] = {};
							return unID;
						}
						i++;
					}
				},
				
				_buildHTML = function() {
					
					$video = $('<video />')
								.attr('id', settings.name)
								.attr('width', settings.width)
								.attr('height', settings.height)
								.attr('poster', settings.poster)
								.attr('src', file);
								
					if(!settings.controls) { $video.attr('controls', true); }
					
					$.each(settings.html.formats, function(i, format) {
						
						var $source = $('<source />')
										.attr('type', format.type)
										.attr('src', format.src);
										
						$video.append($source);
						
					});
					
					$this.replaceWith($this = $video);
					
					window[unID].player = $this.get()[0];
					
				},
				
				_buildFlash = function() {
					
					switch(settings.flash.player) {
						case 'jwplayer':
						
							settings.flashvars = {
								file		: file,
								image		: settings.poster,
								
								playerready	: (unID + '_playerReady'),
								
								skin		: settings.flash.jwplayer.skin,
								controlbar	: (settings.controls) ? 'none' : settings.flash.jwplayer.controlbar,
								dock		: (settings.controls) ? 'none' : settings.flash.jwplayer.dock,
								icons		: (settings.controls) ? 'none' : settings.flash.jwplayer.icons,
								backcolor	: settings.flash.jwplayer.backcolor,
								frontcolor	: settings.flash.jwplayer.frontcolor,
								lightcolor	: settings.flash.jwplayer.lightcolor,
								screencolor	: settings.flash.jwplayer.screencolor,
								shuffle		: settings.flash.jwplayer.shuffle
							};
							
							settings.params = {
								allowfullscreen		: settings.flash.allowfullscreen,
								allowscriptaccess	: settings.flash.allowscriptaccess,
								allownetworking		: settings.flash.allownetworking,
								wmode				: settings.flash.wmode
							
							};
							
							swfobject.embedSWF(
								settings.flash.jwplayer.player,
								settings.name,
								settings.width,
								settings.height,
								settings.flash.version,
								settings.flash.expressinstall,
								settings.flashvars,
								settings.params,
								settings.attributes,
								function(obj) {
									$this = $('#' + obj.id);
								}
							);
							
							window[unID + '_playerReady'] = function() {
								window[unID].player = $this.get()[0];
								
								_addListeners();
								_buildPlaylist();
								_buildCurrent();
								
								if($.isFunction(settings.callbacks.after)) { settings.callbacks.after.call(); }
							};
							
							break;
							
						case 'flowplayer':
						
							break;
					}

				},
				
				_buildDefault = function(callback) {
							
					// If there's an image we'll load that and link it..
					if(settings.flashvars.image) {
						var $image	= $('<img />')
										.attr('src', settings.flashvars.image);
						var $link	= $('<a />')
										.attr('href', file)
										.addClass('player-placeholder-image');
						
						$this.html($link.append($image));

					// ..if not, we'll just load some lame ass text and link it
					} else {
						$this.html('<a href="' + file + '" class="player-placeholder-text">' + settings.link_text + '</a>');
					}
					
				},
				
				_buildControls = function() {
					
					// Create HTML Controls
					$controls = $('<div class="player-controls">');

					$position = $('<a class="player-controls-position">00:00</a>');

					$duration = $('<a class="player-controls-duration">00:00</a>');

					$play = $('<a class="player-controls-play">Play</a>').click(function() {
						if(status.state == 'PLAYING') {
							play();
						} else {
							pause();
						}
					});

					$stop = $('<a class="player-controls-stop">Stop</a>').click(function() {
						stop();
					});
					
					$previous = $('<a class="player-controls-previous">Previous</a>').click(function() {
						previous();
					});
					
					$next = $('<a class="player-controls-next">Next</a>').click(function() {
						next();
					});
					
					$fullscreen = $('<a class="player-controls-fullscreen">Fullscreen</a>').click(function() {
						fullscreen();
					});

					$volume = $('<div class="player-controls-volume"></div>').slider({
						range		: 'min',
						min			: 0,
						max			: 100,
						step		: 0.01,
						value		: settings.volume,
						orientation	: settings.volume_orientation,
						slide		: function(event, ui) {
							setVolume(ui.value);
						}
					});

					$mute = $('<a class="player-controls-mute">Mute</a>').click(function() {
						mute();
					});

					$seek = $('<div class="player-controls-seek"><div class="inner"></div></div>')
						.find('.inner').slider({
							range		: 'min',
							min			: 0,
							max			: 100,
							step		: 0.1,
							slide		: function(event, ui) {
								setSeek(ui.value);
							}
						}).end();

					$controls.append($play, $stop, $previous, $next, $fullscreen, $volume, $mute, $position, $duration, $seek);

					$container = $('<div id="' + $this.attr('id') + '-player-container" class="player-container" />');

					$container = $this.wrap($container).after($controls).parent('.player-container');
					
					window[unID].controls				= $controls.get()[0];
					
					window[unID].controls.play			= $play.get()[0];
					window[unID].controls.stop			= $stop.get()[0];
					window[unID].controls.previous		= $previous.get()[0];
					window[unID].controls.next			= $next.get()[0];
					window[unID].controls.fullscreen	= $fullscreen.get()[0];
					window[unID].controls.mute			= $mute.get()[0];
					window[unID].controls.volume		= $volume.get()[0];
					window[unID].controls.position		= $position.get()[0];
					window[unID].controls.duration		= $duration.get()[0];
					window[unID].controls.seek			= $seek.find('.inner').get()[0];
					
					window[unID].container				= $container.get()[0];
					
					// Set functionality of the controls
					switch(settings.mode) {
						case 'html':
							play = function(){
								window[unID].player.play();
							};
							
							break;
							
						case 'flash':
							play = function() {
								window[unID].player.sendEvent('PLAY', true);
							};
							
							pause = function() {
								window[unID].player.sendEvent('PLAY', false);
							};
							
							previous = function() {
								window[unID].player.sendEvent('PREV');
							};
							
							next = function() {
								window[unID].player.sendEvent('NEXT');
							};
			
							stop = function() {
								window[unID].player.sendEvent('STOP');
							};
			
							setSeek = function(position) {
								window[unID].player.sendEvent('SEEK', (position/100)*status.duration);
							};
			
							setSeekBar = function(position) {
								$(window[unID].controls.seek).slider('value', position);
							};
			
							setVolume = function(volume) {
								window[unID].player.sendEvent('VOLUME', volume);
							};
			
							setVolumeBar = function(volume) {
								$(window[unID].controls.volume).slider('value', volume);
							};
			
							mute = function() {
								window[unID].player.sendEvent('MUTE');
							};
			
							fullscreen = function() {
								
								// JW Player doesn't have a 'fullscreen' that is accessible
								// with Javascript, so this is taking place of a listener
								var style = {};
								
								if(!status.fullscreen) {
									style = {
										position	: 'fixed',
										left		: 0,
										top			: 0,
										width		: '100%',
										height		: '100%'
									};
									
									$(window[unID].container).addClass('fullscreen');
									status.fullscreen = true;
								} else {
									style = {
										position	: 'static',
										left		: 'auto',
										top			: 'auto',
										width		: 'auto',
										height		: 'auto'
									};
									
									$(window[unID].container).addClass('fullscreen');
									status.fullscreen = false;
								}
								
								$(window[unID].container).css(style);
								$(window[unID].player).css(style);
								
							};
							
							load = function(location) {
								
								if(typeof location == 'number') {
									window[unID].player.sendEvent('ITEM', location);
								} else {
									window[unID].player.sendEvent('LOAD', location);
								}
								
							};
							
							break;
							
					}
					
				},
				
				_buildPlaylist = function() {
					
					switch(settings.mode) {
						case 'html':
							break;
						case 'flash':
							
							var playlist = window[unID].player.getPlaylist(),
								tracks = {};
							
							$playlist = $('<ol class="player-playlist" />');
							
							$.each(playlist, function(i) {
								$track = $('<li class="player-playlist-track track-' + i + '" />')
											.click(function(e) {
												e.preventDefault();
												
												load(i);
											})
											.append('<span class="title">' + playlist[i].title + '</span>')
											.append('<span class="duration">' + playlist[i].duration + '</span>');
								
								$playlist.append($track);
							});
							
							$controls.after($playlist);
							
							window[unID].playlist = $playlist.get()[0];
							
							break;
						case 'default':
							break;
					}
					
				},
				
				_buildCurrent = function() {
					
					$current = $('<div class="player-current" />');
					$image = $('<img class="player-current-image" />');
					$title = $('<span class="player-current-title" />');
					
					$current.append($image, $title);
					
					$controls.before($current);
					
					window[unID].current = $current.get()[0];
					
					var playlist = window[unID].player.getPlaylist();
					var track = playlist[0];
					
					$title.text(track.title);
					$image.attr('src', track.image);
					
				},

				_addListeners = function() {
					
					switch(settings.mode) {
						case 'html':
						
							break;
						
						case 'flash':
							
							if(window[unID].player) {
								
								window[unID + '_stateListener'] = function(data) {

									switch(data.newstate) {
										case 'COMPLETED':
											stop();
											break;
										case 'PLAYING':
											$(window[unID].controls.play).addClass('player-controls-pause').text('Pause');
											break;
										case 'PAUSED':
											$(window[unID].controls.play).removeClass('player-controls-pause').text('Play');
											break;
										case 'IDLE':
											$(window[unID].controls.play).removeClass('player-controls-pause').text('Play');
											setSeekBar(0);
											break;
										case 'BUFFERING':
											$(window[unID].controls.play).addClass('player-controls-pause').text('Pause');
											break;
									}

									$(window[unID].controls.container)
										.removeClass(data.oldstate.toLowerCase())
										.addClass(data.newstate.toLowerCase());
								};

								window[unID + '_itemListener'] = function(data) {

									if(data.index != status.current) {
										var playlist = window[unID].player.getPlaylist();
										var track = playlist[data.index];
										
										status.previous = status.current;
										status.current = data.index;
										
										$title.text(track.title);
										$image.attr('src', track.image);
									}

								};

								window[unID + '_timeListener'] = function(data) {
									setSeekBar((data.position / data.duration)*100);
									$(window[unID].controls.position).text(_formatTime(data.position));
									$(window[unID].controls.duration).text(_formatTime(data.duration));

									// Keep track of current data
									status.position = data.position;
									status.duration = data.duration;
								};

								window[unID + '_volumeListener'] = function(data) {
									setVolumeBar(data.percentage);

									// Keep track of volume
									status.volume = data.percentage;
								};

								window[unID + '_muteListener'] = function(data) {

									if(data.percentage === 0) {
										$(window[unID].controls.mute).addClass('player-controls-unmute').text('Unmute');
									} else {
										$(window[unID].controls.mute).removeClass('player-controls-unmute').text('Mute');
									}

								};

								window[unID].player.addModelListener('STATE', unID + '_stateListener');
								window[unID].player.addControllerListener('ITEM', unID + '_itemListener');
								window[unID].player.addModelListener('TIME', unID + '_timeListener');
								window[unID].player.addControllerListener('VOLUME', unID + '_volumeListener');
								window[unID].player.addControllerListener('VOLUME', unID + '_muteListener');
								
							} else {
								
								_addListeners();
								
							}

							break;
						
					}

			},
			
			_formatTime = function(initial){
				var result = '';
				
				_pad = function(number, length) {
		
					var str = '' + number;
					while (str.length < length) {
						str = '0' + str;
					}
		
					return str;
		
				};
				
		
				m = Math.floor(initial/60);
				s = Math.floor(initial%60);
				
				result = _pad(m,2) + ':' + _pad(s,2);
		
				return result;
			};
				
			_init();
			
		});
		
	};
	
	$.fn.player.defaults = {
		mode				: 'auto', // html5, flash, default:auto
		controls			: false, // false relies on player's default controls, true triggers the creation of html controls
		width				: 'auto', // if set to 'auto', the bounds of the player will be set by the parent object
		height				: 'auto', // if set to 'auto', the bounds of the player will be set by the parent object
		min_width			: 320, // if the parent object contains no space, this minimum will be upheld
		min_height			: 240, // if the parent object contains no space, this minimum will be upheld
		poster				: '',
		
		autostart			: false, // if the player shall start upon loading
		volume				: 100, // initial volume of the player
		volume_orientation	: 'horizontal', // orentation of the slider built for the player controls
		
		link_text			: 'Watch the Video', // default text if player should degrade
		error_text			: 'Sorry, but the video cannot be played on your device', // error to be displayed upon not being able to load the video
		about_text			: 'ground(ctrl)',
		about_link			: 'http://www.groundctrl.com/',
		
		html				: {
			formats				: {
				mp4					: {
					type				: 'video/mp4',
					src					: ''
				},
				webm				: {
					type				: 'video/ogg',
					src					: ''
				},
				ogg					: {
					type				: 'video/ogg',
					src					: ''
				}
			}
		},
		
		flash				: { // Flash defaults
			player				: 'jwplayer',
			
			jwplayer			: {
				player				: '/engine/swf/jw/player.swf',
				skin				: '/engine/swf/jw/skins/glow/glow.xml',
				controlbar			: 'over',
				dock				: true,
				icons				: true,
				backcolor			: 'ffffff',
				frontcolor			: '000000',
				lightcolor			: '000000',
				sreencolor			: '000000',
				shuffle				: false
			},
			
			express_install		: '/engine/swf/expressisntall.swf',
			version				: '9.0.0',
			
			allowfullscreen		: true,
			allowscriptaccess	: 'always',
			allownetworking		: 'all',
			
			wmode				: 'transparent'
		},
		
		callbacks			: {
			before				: null,
			after				: null
		}
	};
	
})(jQuery);

