/**
 * The core component of the zPlayer.
 *
 * @author Joey Line <joey.line@skiclubz.com>
 */
(function($) {
	var players = {};
	
	// jQuery plugin-like init function
	$.fn.zPlayer = function(options) {
		return this.each(function() {
			if(!players[this.id])
				players[this.id] = new zPlayer(this, options);
			return players[this.id];
		});
	};
	
	$.zPlayer = function(id) {
		return players[id];
	};
	
	// constructor for zPlayer object
	zPlayer = function(element, options) {
		this.domelement = element;
		this.id = element.id;
		element.player = this;

		$.extend(this.config, options);
		zPlayer.utils.log('zPlayer initialized');
		
		// find the "unsupported browser/no Flash" message and hide it. if it does not exist yet,
		// create it and keep it hidden.
		this.unsupportedDiv = $(element).childrenOrSiblings('.unsupportedMessage');
		if(!zPlayer.utils.isNull(this.unsupportedDiv[0])) {
			this.unsupportedDiv.css('display', 'none');
		} else {
			this.unsupportedDiv = $('<div/>')
				.addClass('unsupportedMessage')
				.html(unescape(this.config.unsupportedMessage))
				.css('display', 'none')
				.appendTo(this.config.container);
		}
		
		return this.setup();
	};
	
	zPlayer.prototype = {
		id: undefined,
		domelement: undefined,
		container: undefined,
		sources: undefined,
		plugins: undefined,
		source: 0,
		playing: false,
		seeked: false,
		time: 0,
		player: undefined,
		endBuffer: 0,
		bufferTimeout: undefined,
		
		// the skin object, which holds all the components, elements and images
		skin: undefined,
		
		// the display object, which holds the controlbar and possibly more in the future
		display: undefined,
		
		flashString: '<object type="application/x-shockwave-flash" %attributes% %style%> <param name="movie" value="%flashplayer%"> <param name="allowfullscreen" value="true"> <param name="allowscriptaccess" value="always"> <param name="wmode" value="transparent"> <param name="flashvars" value="%flashvars%"> </object>',

		
		// TODO: Add more options [see flashvars/jwplayer]
		config: {
			preferredPlayer: 'html5', // html5 | flash
			autostart: true,
			rate: 1.0,
			stepSize: 0.033333,
			fontSize: 11,
			loop: false,
			displayClick: 'play', // play | fullscreen
			container: '.player',
			plugins: null,
			html5plugins: null,
			pluginDir: null,
			unsupportedMessage: 'Please upgrade to a recent browser such as Safari 4.0 or install Adobe Flash for your browser.',
			playerready: 'playerReady',
			enableFullscreen: true
		}
	};

	/**
	 * Sets up listeners for various html5 player related events.
	 */
	zPlayer.prototype.setup = function() {
		this.container = $(this.domelement).parents(this.config.container);
		this.sources = {};
		this.display = {};
		var sourceSrcs = {};

		// find sources [from src or <source> children] and save to array for later usage
		if(this.domelement.src) {
			var source = document.createElement('source');
			source.src = this.domelement.src;
			this.sources[0] = source;
			sourceSrcs[0] = $(source).attr('src');
			zPlayer.utils.log('src', this.domelement.src, source, sourceSrcs);
		} else {
			// check for child <source> elements and if the browser can play them
			$(this.domelement).childrenOrSiblings('source').each($.proxy(function(i, source) {
				this.sources[i] = source;
				sourceSrcs[i] = $(source).attr('src');
				zPlayer.utils.log('sources', sourceSrcs);
			}, this));
		}
		
		var loaded = false;
		if(this.config.preferredPlayer=='html5') {
			loaded = this.loadHtml5();
		} else {
			loaded = this.loadFlash();
		}
		
		if(!loaded) {
			zPlayer.utils.serverLog('Error loading ZPlayer', sourceSrcs, $.browser, navigator.userAgent, 'domelement.canPlayType: ' + !!this.domelement.canPlayType, 'supports Flash: ' + zPlayer.utils.supportsFlash(), this.config.flashplayer);
			
			// no Flash and no HTML5 video - show error message!
			// alert('no Flash and no HTML5'); // FIXME: better message/presentation needed
			this.unsupportedDiv.css('display', 'block');
			return false;
		}
	};
	
	// TODO: This and loadFlash could be moved to their own files/subobjects
	zPlayer.prototype.loadHtml5 = function(triedFlash) {
		zPlayer.utils.log('in loadHtml5');
		if(zPlayer.utils.checkCompatibleBrowser() && this.domelement.canPlayType) {
			// the browser can play html5 media, now check if it can play our sources
			var canPlay = false;
			
			var source, sourceIndex;
			for(sourceIndex in this.sources) {
				source = this.sources[sourceIndex];
				if(zPlayer.utils.isNull(source.type)) {
					var extension = zPlayer.utils.extension($(source).attr('src'));
					if(extension=='ogv') {
						extension = 'ogg';
					}
					
					source.type = 'video/' + extension + ';';
				}
				
				// check if the browser can play the video's src
				try {
					canPlay = !!this.domelement.canPlayType(source.type);
				} catch (e) { }
				zPlayer.utils.log(source, source.type, canPlay);
				if(canPlay) {
					this.source = source;
					break;
				}
			}
			
			if(canPlay) { // HTML5 player works!
				// set up some event listeners
				// listen for timeupdates
				this.domelement.addEventListener('timeupdate', function(evt) {
					// zPlayer.utils.log('timeupdate', evt.target.currentTime);
					// time = evt.target.currentTime;
				}, false);
				
				// keep track of the playing state
				this.domelement.addEventListener('play', function(e) {
					this.player.playing = true;
					zPlayer.utils.log('video was started playing, saved state.', this.player.playing, e);
				}, false);
				this.domelement.addEventListener('pause', function() {
					this.player.playing = false;
					zPlayer.utils.log('video was paused, saved state.', this.player.playing);
				}, false);
				this.domelement.addEventListener('ended', function() {
					this.player.playing = this.loop;
					zPlayer.utils.log('video has ended, saved state.', this.player.playing);
				}, false);
				
				// display click replacement
				$(this.domelement).click(function() {
					if(this.paused) {
						this.play();
					} else {
						this.pause();
					}
				});
				$(this.domelement).css('cursor', 'pointer');
				
				// save that we're using the html5 player.
				this.player = 'html5';
				
				if(!zPlayer.utils.isiPhone()) {
					// load skin
					this.skinner(function(player) {
						zPlayer.utils.log('skinner callback called', player, player.id);
						
						// initialize the controlbar
						player.controlbar();
						
						zPlayer.utils.log('Controlbar loaded', player.id);
						
						// load plugins via the pluginManager
						player.pluginManager(function(player) {
							zPlayer.utils.log('pluginManager callback called', player.id);
							
							// set up the resizeHandler
							$(document).bind('fullscreen.zplayer', player.resizeHandler(player));
							
							// display the controlbar
							player.controlbar.display(player);
							// player.display.controlbar.element.css('display', '');
							
							if(!zPlayer.utils.isNull(player.config.playerready) && typeof window[player.config.playerready] == 'function') {
								window[player.config.playerready](player);
							}
							
							if(player.config.autostart) {
								player.domelement.autoplay = true;
								player.domelement.play();
							}
						});
						
						// XXX: This is just for testing. On the other hand, it may be useful in the
						// future.
						// var events = {
							// 'abort': zPlayer.eventHandler,
							// 'canplay': zPlayer.eventHandler,
							// 'canplaythrough': zPlayer.eventHandler,
							// 'durationchange': zPlayer.eventHandler,
							// 'emptied': zPlayer.eventHandler,
							// 'ended': zPlayer.eventHandler,
							// 'error': zPlayer.eventHandler,
							// 'loadeddata': zPlayer.eventHandler,
							// 'loadedmetadata': zPlayer.eventHandler,
							// 'loadstart': zPlayer.eventHandler,
							// 'pause': zPlayer.eventHandler,
							// 'play': zPlayer.eventHandler,
							// 'playing': zPlayer.eventHandler,
							// 'progress': zPlayer.eventHandler,
							// 'ratechange': zPlayer.eventHandler,
							// 'seeked': zPlayer.eventHandler,
							// 'seeking': zPlayer.eventHandler,
							// 'stalled': zPlayer.eventHandler,
							// 'suspend': zPlayer.eventHandler,
							// 'timeupdate': zPlayer.eventHandler,
							// 'volumechange': zPlayer.eventHandler,
							// 'waiting': zPlayer.eventHandler,
							// 'canshowcurrentframe': zPlayer.eventHandler,
							// 'dataunavailable': zPlayer.eventHandler,
							// 'empty': zPlayer.eventHandler,
							// 'load': zPlayer.eventHandler,
							// 'loadedfirstframe': zPlayer.eventHandler
						// };
						// $.each(events, function(type, handler) {
							// player.domelement.addEventListener(type, handler, false);
						// });
					});
				}
				
				return true;
			}
		}
		
		if(!triedFlash) {
			// HTML5 video doesn't work and Flash hasn't been tried yet, so try Flash
			return this.loadFlash(true);
		}
		
		return false;
	};
	
	zPlayer.eventHandler = function(e) {
		zPlayer.utils.log(e.type, e, e.currentTarget.player);
	};
	
	zPlayer.prototype.loadFlash = function(triedHtml5) {
		zPlayer.utils.log('in loadFlash');
		// check if the browser supports flash in the first place
		if(!zPlayer.utils.isNull(this.config.flashplayer) && zPlayer.utils.supportsFlash()) {
			zPlayer.utils.log(this.sources);
			
			// look for a source that flash can play
			var sourceIndex, source;
			for(sourceIndex in this.sources) {
				source = this.sources[sourceIndex];
				
				if(zPlayer.utils.flashCanPlay(zPlayer.utils.extension(source.src))) {
					this.source = source;
					break;
				}
			}
			
			// if a playable source is found, load it with jwPlayer.
			if(this.source) {
				zPlayer.utils.log('playable source found...');
				var flashString = this.flashString,
					attributeString = 'data="' + this.config.flashplayer + '" ',
					flashvarString = 'file=' + this.source.src + '&';
				
				if(!zPlayer.utils.isNull(this.id)) {
					attributeString += 'id="' + this.id + '" name="' + this.id + '" ';
				}
				
				for (var flashvar in this.config) {
					if (!zPlayer.utils.isNull(this.config[flashvar])) {
						if(flashvar != 'file' && flashvar != 'unsupportedMessage') {
							flashvarString += flashvar + '=' + this.config[flashvar] + '&';
						}
					}
				}
				
				if(this.config.height.indexOf('px') >= 0) {
					var height = (25 + parseInt(this.config.height.replace('px', ''), 10)) + 'px';
				} else {
					var height = this.config.height;
				}
				
				flashString = flashString.replace('%attributes%', attributeString)
					.replace('%flashvars%', flashvarString)
					.replace('%flashplayer%', this.config.flashplayer)
					.replace('%style%', 'style="left:0;top:0;position:absolute;z-index:1;width:100%;height:100%;"');
				
				$(this.container).css('height', height)
					.css('width', this.config.width)
					.css('position', 'relative')
					.append(flashString);
				
				// change the ID of the HTML5 Video element so [outside] scripts don't get confused
				this.domelement.id = this.domelement.id + 'html5';
				
				this.player = 'flash';
				
				// show message to switch to a more modern browser
				if(this.config.preferredPlayer != 'flash' && this.config.upgradeBrowserMessage) {
					this.container.after('<div id="upgradeBrowserMessage">' + unescape(this.config.upgradeBrowserMessage) + '</div>');
				}
				
				return true;
			}
		}
		
		if(!triedHtml5) {
			// Flash isn't supported or doesn't play our sources and HTML5 hasn't been tried yet,
			// so try HTML5
			return this.loadHtml5(true);
		}
		
		return false;
	};
	
	// this is on zPlayer and not zPlayer.controlbar because it handles the player itself
	// going to fullscreen and back to normal size.
	// NOTE: This gets called as an actual event handler now, using jQuery.bind() and .trigger()
	zPlayer.prototype.resizeHandler = function(player) {
		return function(eObj, fullscreen) {
			var domelement = $(player.domelement),
				resizeHandler = function() { // fullscreen window resize handler
					if(domelement[0].player.fullscreen) {
						zPlayer.utils.log('resizeHandler');
						domelement[0].player.controlbar.resizeHandler(domelement[0].player, {
							fullscreen: true
						});
					}
				};
			
			if(fullscreen) {
				player.container.css('position', 'fixed')
					.css('width', '100%')
					.css('height', '100%')
					.css('top', '0')
					.css('left', '0')
					.css('z-index', '999')
					.css('background-color', '#000');
				
				domelement.css('width', '100%')
					.css('height', '100%')
					.css('display', 'block');
				
				player.fullscreen = true;
				
				// listen for window resize events
				$(window).bind('resize', resizeHandler);
			} else {
				if(player.config.height.indexOf('px') >= 0) {
					var height = (player.display.controlbar.element.height() + parseInt(player.config.height.replace('px', ''), 10)) + 'px';
				} else {
					var height = player.config.height;
				}
				
				player.container.css('position', 'relative')
					.css('width', player.config.width)
					.css('height', height)
					.css('z-index', '')
					.css('background-color', 'transparent');
				
				domelement.css('width', '100%')
					.css('height', 'auto')
					.css('display', 'block');
				
				// remove event listeners for window resize events
				// FIXME: Safari doesn't seem to like unbinding the function by name, so we're just
				// putting a check in the event handler [player.fullscreen] to make sure the event only
				// triggers when desired. However, it would be better to unbind the handler.
				// $(window).unbind('resize', resizeHandler);
				player.fullscreen = false;
			}
			
			player.controlbar.resizeHandler(player, fullscreen);
		}
	};
	
	zPlayer.prototype.sendEvent = function(name, data) {
		if(this.player=='html5') {
			switch (name) {
				case 'PLAY':
					if(data) {
						this.domelement.play();
					} else {
						this.domelement.pause();
					}
					break;
				case 'RESIZE':
					this.controlbar.resizeHandler(this, this.fullscreen);
					break;
				case 'STOP':
					this.domelement.pause();
					this.domelement.currentTime = 0;
					break;
				// FIXME: These really shouldn't be here, but this whole thing is kind of a hack to
				// get splitscreen "both" buttons working with both JWPlayer and zPlayer anyway...
				case 'STEPBACK':
					this.display.controlbar.elements.stepBack.click();
					break;
				case 'STEPFORWARD':
					this.display.controlbar.elements.stepForward.click();
					break;
			};
		} else {
			// make sure slowmo is off first
			$('#' + this.id)[0].sendEvent('SLOWMO', false);
			$('#' + this.id)[0].sendEvent(name, data);
		}
	};
})(jQuery);

