(function($){
    
    $.fn.H5Video = function(opt) {
        var options = $.extend(true, {
            animationDuration : 400,
            preload : false,
            events : {}
        }, opt);    

        return this.each(function() {
            var context = {
            	options   : options, 
            	target    : jQuery(this),
            	video     : jQuery(document.createElement("video")),
            	jig		  : jQuery(document.createElement("div"  )).attr({ "class": "h5video"}),
            	poster    : jQuery(document.createElement("div"  )).attr({ "class": "poster" }),
            	controls  : null,
            	animating : false,
            	buttons   : {}
            };
            
            if (options.preload)
            	context.video.attr({ "autobuffer": "true", "preload" : "true" });
            
			if (initialize(context))
			{
				if (context.options.autoPlay) 
					play({ data : context });

			}
			else
			{
				buildBrowserSupportMessage(context);
			}
        });
    };

    function initialize(context) {
		try
		{
			var videoSources = context.options.source;
			for (var source in videoSources)
			{
				var domVideo = context.video[0];
				if (domVideo.canPlayType(source))
				{
					context.jig.append(context.video);
					context.video.attr("src", videoSources[source]);

					if (context.options.loop) 
						context.video.attr("loop", "true");
	
					jQuery(document.body).append(context.jig);
	
					assignVideoEvents(context);

					if (!isIpad() && !isIphoneOrIpod())
					{
						if (context.options.poster) 
						{
							context.poster.css("background-image", "url(" + context.options.poster + ")");
							context.jig.append(context.poster);
						}

						buildControl(context);
						minimize    (context);
					}
					else
					{
						context.video.attr("controls", "controls");
					}	
					
					return true;
				}
			}
		}
		catch(e) {
		}
    };
    
    function isIpad()
    {
    	return navigator.userAgent.match(/iPad/i) != null;
    };

    function isIphoneOrIpod()
    {
    	return (navigator.userAgent.match(/iPhone/i) != null || navigator.userAgent.match(/iPod/i) != null);
    };

	function buildBrowserSupportMessage(context)
	{
		context.target.append(jQuery(document.createElement("div")).attr( 'id', 'mySWFVideo' ) );
		swfobject.embedSWF(
			'/video/data/video.swf',
			'mySWFVideo',
			720,
			405,
			'9.0.0',
			'/swfobject/expressInstall.swf',
			null,
			null,
			null,
			function(e){ 
				$( '.modalCloseImg' ).css( 'top', 0 );
				if( e.success === false ) { 
					var errorMessage = (context.options.supportMessage) ? context.options.supportMessage : "This browser is not able to playback HTML5 videos. We encourage you to upgrade your internet browser to one of the following modern and more secure browsers:";
					context.target.append(jQuery(document.createElement("div")).addClass("support-message").append(
							'<p>' + errorMessage + '</p>' +
							'<p>' + 
								'<a href="http://getfirefox.com"><img src="images/browser_firefox.png" />Firefox</a>' +
								'<a href="http://www.google.com/chrome"><img src="images/browser_chrome.png" />Chrome</a>' +
								'<a href="http://www.apple.com/safari"><img src="images/browser_safari.png" />Safari</a>' +
								'<a href="http://www.opera.com/"><img src="images/browser_opera.png" />Opera</a>' +
							'</p>'
						)
					);	
				}
			}
		);
	};
	
    function buildControl(context)
    {

    	context.controls = jQuery(document.createElement("div")).attr({ "class" : "control-panel" });
    	context.jig.append(context.controls);

    	var btnPlay = jQuery(document.createElement("a")).attr({ "class" : "play" , "href" : "", "onclick": "return false;" }).text(" ").bind("click", context, play);
    	context.controls.append(btnPlay);
    	
    	var seekBar = jQuery(document.createElement("div")).attr({ "class" : "seek-bar rounded" }).bind("click", context, seek);
    	context.controls.append(seekBar);
    	
    	var buffer = jQuery(document.createElement("div")).attr({ "class" : "seek-bar-buffer rounded" });
    	seekBar.append(buffer);

    	var guage = jQuery(document.createElement("div")).attr({ "class" : "seek-bar-guage rounded" });
    	seekBar.append(guage);

    	var btnFullscreen = jQuery(document.createElement("a")).attr({  "class" : "size full" , "href" : "", "onclick": "return false;" }).text(" ").bind("click", context, resize);
    	context.controls.append(btnFullscreen);

    	var btnMute = jQuery(document.createElement("a")).attr({  "class" : "muter mute" , "href" : "", "onclick": "return false;" }).text(" ").bind("click", context, mute);
    	context.controls.append(btnMute);

    	var interval = jQuery(document.createElement("span")).attr({ "class" : "interval" }).text("00:00 / 00:00");
    	context.controls.append(interval);
    	

    	context.buttons.play       = btnPlay;
    	context.buttons.fullscreen = btnFullscreen;
    	context.buttons.mute       = btnMute;
    	context.buttons.interval   = interval;
    	context.buttons.seekBar    = seekBar;
    	context.buttons.buffer     = buffer;
    	context.buttons.guage      = guage;
    	
    	updateSeekbarWidth(context);
    };

	function assignVideoEvents(context)
	{
		context.video.bind("error"         , context, handleError     );	
		context.video.bind("volumechange"  , context, mute            );
		context.video.bind("waiting"       , context, waiting         );
		context.video.bind("stalled"       , context, waiting         );
		context.video.bind("canplay"       , context, resumePlay      );
		context.video.bind("playing"       , context, onPlayingStart  );
		context.video.bind("pause"         , context, onPause         );	
		context.video.bind("ended"         , context, onEnd           );	
		context.video.bind("loadedmetadata", context, onLoadedMetadata);	
		context.video.bind("timeupdate"    , context, onDurationChange);
		context.video.bind("progress"      , context, onProgress      );
		
    	context.jig.bind("mouseenter", context, showControls);
    	context.jig.bind("mouseleave", context, hideControls);

		var left = 0;
		window.setInterval(function(){
			if (!isMaximized(context))
				minimize(context);
		},100);
	};

	function minimize(context)
	{
		var offset = context.target.offset();
		var width  = context.target.outerWidth();
		var height = context.target.outerHeight();
		
		context.jig.css({
			"position" : "absolute",
			"width"    : width       + "px",
			"height"   : height      + "px",
			"top"      : offset.top  + "px",
			"left"     : offset.left + "px"
		}); 

		if (isIpad()  || isIphoneOrIpod())
			return;
			
		context.buttons.fullscreen.removeClass("shrink").addClass("expand");
		context.jig.css("background-color", "transparent");
		updateSeekbarWidth(context);
	};

	function maximize(context)
	{
		context.jig.css({
			"position" : "fixed",
			"width"    : "100%",
			"height"   : "100%",
			"top"      : "0px",
			"left"     : "0px"
		}); 

		context.buttons.fullscreen.removeClass("expand").addClass("shrink");
		context.jig.css("background-color", "#000");
    	updateSeekbarWidth(context);
	};

	function updateSeekbarWidth(context)
	{
		var o = context.buttons;
		var width = (o.play.width() + o.fullscreen.width() + o.mute.width() + o.interval[0].offsetWidth);

		width = context.controls.width() - width - 15; 
		
    	o.seekBar.css("width", width + "px");
	};
    
    function showControls(e)
    {
		var context = e.data;
		updateSeekbarWidth(context);
		
		if (context.animating == false)
		{
			context.animating = true;
			e.data.controls.fadeTo(context.options.animationDuration, 1.0, function(){ context.animating = false; });
		}
    };

    function hideControls(e)
    {
		var context = e.data;	
		if (context.animating == false && context.controls.find(".play").length == 0)
		{
			context.animating = true;
	    	context.controls.fadeTo(context.options.animationDuration, 0.0, function(){ context.animating = false; });
		}
    };

	function isMaximized(context)	
	{
		if (isIpad() || isIphoneOrIpod())
			return false;
		
		return context.buttons.fullscreen.hasClass("shrink");
	};

	function onPlayingStart(e)
	{
		var context = e.data;
		
		if (!isIpad() && !isIphoneOrIpod())
		{
			context.controls.find(".play").removeClass("play").addClass("pause");
			context.poster.fadeOut(context.options.animationDuration);
		}
		
		callUserCallback(context, "play");
	};
	
	function waiting(e)
	{
		if (!isIpad() && !isIphoneOrIpod())
		{
			var context = e.data;
		}
	};
	
	function resumePlay(e)
	{
		var context = e.data;
	};
	
	function secondsToTime(duration)
	{
		var minutes  = 0;

		if (duration < 60)
		{
			var second = Math.floor(duration);
			if (isNaN(second))
				return "00:00";
			
			minutes = "00:" + ((second < 10) ? "0" + second : second);
		}
		else
		{
			minutes = Math.floor(duration / 60);
			if (minutes < 10)
				minutes = "0" + minutes;
				
			var remainingMinutes = Math.floor(duration % 60);

			if (isNaN(minutes) || isNaN(remainingMinutes))
				return "00:00";

			minutes += ":" + ((remainingMinutes < 10) ? "0" + remainingMinutes : remainingMinutes); 
		}
		
		return minutes;
	};
	
	function onDurationChange(e)
	{
		if (!isIpad() && !isIphoneOrIpod())
		{
			var context         = e.data; 
			var percentComplete = Math.ceil((e.target.currentTime / e.target.duration) * 100);
			context.controls.find(".seek-bar-guage").css("width", percentComplete + "%");	
	
			var currentTime = secondsToTime(e.target.currentTime) + " / " + secondsToTime(e.target.duration); 
			context.controls.find(".interval").text(currentTime);	
		}
	};
	
	function onProgress(e)
	{
		if (!isIpad() && !isIphoneOrIpod())
		{
			var context = e.data; 
			
			try
			{
				var percentComplete = Math.ceil((e.originalEvent.loaded / e.originalEvent.total) * 100);
				
				if (isNaN(percentComplete))
					percentComplete = Math.ceil((e.target.buffered.end(0) / e.target.duration) * 100);
	
				if (isNaN(percentComplete))
					percentComplete = Math.ceil((e.target.buffered.end(0) / e.target.duration) * 100);
		
				context.controls.find(".seek-bar-buffer").css("width", percentComplete + "%");	
			}
			catch(e) {}
		}
	};
	
	function onPause(e)
	{
		var context = e.data; 

		if (!isIpad() && !isIphoneOrIpod())
			context.controls.find(".pause").removeClass("pause").addClass("play");

		callUserCallback(context, "pause");
	};
	
	function onLoadedMetadata(e)
	{
		if (!isIpad() && !isIphoneOrIpod())
		{
			var context = e.data; 
			var text = secondsToTime(e.target.currentTime) + " / " + secondsToTime(e.target.duration); 
			context.controls.find(".interval").text(text);
		}
	};
	
	function onEnd(e)
	{
		var context = e.data; 

		if (!isIpad() && !isIphoneOrIpod())
		{
			if (isMaximized(context))
				minimize(context);
			
			if (context.options.loop === true && jQuery.browser.mozilla)
			{
				seekToTime(context, 0);
				return;	
			}
				
			context.controls.find(".pause").removeClass("pause").addClass("play");
			context.poster.fadeIn(context.options.animationDuration, function(){
				callUserCallback(context, "end");
			});
			
			showControls(e);
		}	

		callUserCallback(context, "end");
	};

	function play(e)
	{
		var context  = e.data;
		var domVideo = context.video[0];

		if (context.controls.find(".play").length > 0)
		{
			domVideo.play();

			if (jQuery.browser.safari || jQuery.browser.opera)
			{
				// since "playing" event is fired oly once in webkit and opera browsers I call it manually
				onPlayingStart(e);
			}
		}
		else
		{
			domVideo.pause();
		}
		
		return false;
	};
    
	function mute(e)
	{
		var context   = e.data;
		var domVideo  = context.video[0];

		if (!domVideo.muted)
		{
			context.controls.find(".muter").removeClass("un-mute").addClass("mute");
			domVideo.muted = (e.target.tagName == "A");
		}
		else
		{
			context.controls.find(".muter").removeClass("mute").addClass("un-mute");
			domVideo.muted = (e.target.tagName != "A");;
		}
		
		return false;
	};
	
	function resize(e)
	{
		var context = e.data;

		if (isMaximized(context)) minimize(context); else maximize(context);
		
		return false;
	};
	
	function seek(e)
	{
		var context        = e.data; 
		var domVideo       = context.video[0];
		var seekBar        = context.controls.find(".seek-bar");
		
		var seekPixel      = (jQuery.browser.opera) ? e.offsetX : e.layerX - e.target.offsetLeft;
		var seekPercentage = Math.floor((seekPixel / seekBar.width()) * 100);
		var seekTime       = Math.floor((seekPercentage / 100) * domVideo.duration);
		
		if (domVideo.seekable <= seekTime)
		{
			alert("can't seek to yet!");
			return;
		}

		domVideo.pause();
		try { domVideo.currentTime = seekTime; } catch (e) {}
		domVideo.play();
	};
	
	function seekToTime(context, time)
	{
		var domVideo = context.video[0];
		domVideo.pause();
		try { domVideo.currentTime = seekTime; } catch (e) {}
		domVideo.play();
	};
	
	function handleError(e)
	{
		switch (e.target.error.code) {
			case e.target.error.MEDIA_ERR_ABORTED:
				alert('You aborted the video playback.');
				break;

			case e.target.error.MEDIA_ERR_NETWORK:
				alert('A network error caused the video download to fail part-way.');
				break;

			case e.target.error.MEDIA_ERR_DECODE:
				alert('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.');
				break;

			case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
				alert('The video could not be loaded, either because the server or network failed or because the format is not supported.');
				break;

			default:
				alert('An unknown error occurred.');
				break;
		}
	};
	
	function callUserCallback(context, event)
	{
		var events = context.options.events;
		if (events && typeof events[event] == "function")
			window.setTimeout(events[event], 1);
	};
	
})(jQuery);
