/**
 * @author Max Klymyshyn
 * @site http://www.sonettic-cinema.com
 * v. 0.1a
 */
(function($){
	var defaults = {
		flashvars : {
			'content' : '',
			'zholdercornerradius' : 0,
			'zholderpadding' : 5,
			'slavemode' : true,
		    'playerID' : 'mellowblueplayer'
		},
		cinemaPlayerLocation : 'http://sonettic-cinema.com/labs/cinemaplayer-free-dev.swf',
		width : 470,
		height: 264	
	};
	
	var handlers = {};
	var currentIndex = 0;
	var idPrefix = 'mellowbluePlayer';
	$.fn.mellowblue = function(options){
	
		var options = $.extend({}, defaults, options);
		
		return this.each(function(){
			currentIndex += 1;
			var flashvarsParser = new queryStringParser($(this).attr('rel')).parse();
			var playerId = idPrefix + currentIndex;

			// get ID from <a> tag or generate it if it's not available
			if($(this).attr('id') == '' || $('#' + $(this).attr('id')).length > 1){
				$(this).attr('id', playerId);
			}else{
				playerId = $(this).attr('id');
			}

			// add content and ID
			flashvarsParser.add('content', $(this).attr('href'));			
						
			$(generateHTML(playerId)).insertAfter(this);
			$(this).remove();
			interfaceInit(playerId, flashvarsParser);
		});		
			
	};

	/** 
	 * @function
	 * @description add new handler with copy of the Events object
	 * @param id id of the new handler
	 * @param e copy of the Events object
	 * @return boolean
	 */
	function addHandler(id, e){
		handlers[id] = e;
	}
	/**
	 * @function
	 * @description get handler by id
	 * @param id id of the new handler
	 * @return copy of the Events object or null if it's not exist
	 */
	function getHandler(id){
		if(handlers[id])return handlers[id];
		return null;
	}
	

	/**
	 * @function
	 * @description Init video holder, create API object and dispatch basic events
	 * @param id player object id
	 */	
	function interfaceInit(id, flashvarsParser){
		var skinObj = $('#' + id);
		
		// change video object holder ID
		var videoHolderName = id + 'videoholder';
		skinObj.find('.mellowblueVideoObject').attr('id', videoHolderName);	
		
		flashvarsParser.add('playerID', videoHolderName);
		
		// embed video holder and create an SntPlayer instance
		var flashvars = $.extend(defaults.flashvars, flashvarsParser.list());
		playerObjectInit(id, videoHolderName, flashvars)
	}
	
	/** 
	 * @function
	 * @description init player object
	 * @param id player DOM id
	 * @param flashvars arra with flashvars
	 * @return void
	 */
	function playerObjectInit(id, videoHolderId, flashvars){
		// callback
		//SntInitWithSwfobject(id, {'width' : 470, 'height' : 264}, flashvars, initSonetticCinemaJSAPI);
		var player = new SntPlayer(videoHolderId);
		var embed = new SntEmbed(videoHolderId, defaults.cinemaPlayerLocation);
		embed.setFlashvars(flashvars);

		// callback function
		var initCallback = function(){
			createEventsHandler(id, player, videoHolderId);
		};

		embed.setCallback(initCallback);
		
		embed.render(new SntSwfobjectEmbed());	
	}
	
	function createEventsHandler(id, player, videoHolderId){
		//var skinObj = $('#' + id);				
		var handler = new Events(id, player, videoHolderId).init();
		addHandler(id, handler);
	}
		
	/** 
	 * @function
	 * @description Generate HTML code for the Skin interface
	 * @param player ID
	 */
	function generateHTML(id){
		return ' \
		<div class="mellowblue" id="' + id + '"> \
			<span class="t-c"><span class="t-c-l1"><span class="t-c-l2"></span></span></span> \
			<div class="mellowblue-video-holder"> \
				<div class="mv-area"> \
					<div class="mellowblueVideoObject"> \
						<h3>Loading Sonttic-Cinema <a href="http://sonettic-cinema.com/">flash video player</a>...</h3> \
					</div> \
				</div> \
			</div> \
			<div class="b-c"> \
				<div class="b-c-l1"> \
					<span class="mbr mellowblue-control-playback mellowblue-play"> </span> \
					<span class="mbr mellowblue-control-playback-stop mellowblue-stop"> </span> \
				</div> \
				<div class="b-c-l2"> \
					<div class="b-c-l3"> \
						<div class="b-c-l4"> \
							<div class="mellowblue-timeline"> \
								<div class="mlb-timeline"> \
									<span class="mellowblue-tml-pos">00:00</span> \
									<div class="mellowblue-timeline-holder"> \
										<div class="mlb-buffering-slider"> \
											<div class="mlb-timeline-slider"> \
												<div class="mth-c-l1"> \
													<div class="mth-c-l2"> \
														<div class="mth-c-l3"></div> \
													</div> \
												</div> \
											</div> \
										</div> \
									</div> \
									<span class="mellowblue-tml-len">00:00</span> \
								</div> \
								<div class="mellowblue-volume"> \
									<div class="mellowblue-volume-slider"> \
										<div class="mellowblue-control-playback-volume mellowblue-volume-mask">&nbsp;</div> \
									</div> \
								</div> \
							</div> \
						</div> \
 					</div> \
				</div> \
			</div> \
		</div>';	
	}
	
	// private function for debugging
	function debug(msg, obj) {
		if (window.console && window.console.log){
			window.console.log('MellowBlue Skin Debug: ' + msg);
			if(obj != null){
				window.console.log(obj);	
			}
		}
	};
	
		
	
	/** 
	 * @class
	 * @description Object which hooks all player events
	 */
	function Events(id, player, videoHolderId){
		var obj = this;
		var skinObj = $('#' + id);

		// get interface controls
		this.playPauseButton = skinObj.find('.mellowblue-control-playback');
		this.stopButton = skinObj.find('.mellowblue-control-playback-stop');
		this.soundSlider = skinObj.find('.mellowblue-control-playback-volume');
		this.soundLevel = skinObj.find('.mellowblue-volume-slider');
		this.timelineHolder = skinObj.find('.mlb-timeline-slider');
		this.timelineSlider = skinObj.find('.mlb-timeline-slider');
		this.durationText = skinObj.find('.mellowblue-tml-len');
		this.positionText = skinObj.find('.mellowblue-tml-pos');
		this.timelineBuffering = skinObj.find('.mlb-buffering-slider');
		this.player = player;
		
		
		// some default values
		this.soundLevelMax = this.soundLevel.width();
		this.tLen = this.timelineHolder.width();
		this.duration = 0.1;
		this.timelineOriginalLen = 1000;
		this.defaultSoundLevel = 70;

		return this;		
	}
	
	Events.prototype = {
		/**
		 * @function
		 * @description Method decorator to using calls from callback functions (via closures)
		 * @param method String with method name or link to method name
		 */
		bind : function(method){
			var obj = this;
			var fn = null;			
			if(method.constructor.toString().indexOf('Function') != -1 ){
				return function(){ 
					// apply parent arguments to original method
					method.apply(obj, arguments);
				};		
			}
			return function(){
				obj[method].apply(obj, arguments);
			}
		},
		init : function(r){
			var obj = this;
			obj.player.ready(function(){ obj.defineHandlers(obj); });
			return this;
		},		
		// define all handlers for events
		defineHandlers : function(obj){
			var player = obj.player;
			
			// add events to dispatcher
			player.addEventListener('mediaPlay', this.bind(this.onPlayEvent));
			
			player.addEventListener('mediaPause', this.bind(this.onPauseEvent));
			player.addEventListener([ 'mediaStop', 'loadingStop' ], this.bind(this.onStopEvent));
			
			// add handler for jQuery event `click`
			obj.playPauseButton.click(this.bind(this.playEvent));
			// end of play and pause
			
			
			// stop downloading of the movie button (not pause)
			obj.stopButton.click(this.bind(this.stopEvent));
	
	
			// add events to dispatcher
			player.addEventListener('mediaStopStartPosition', this.bind(this.onMediaStopStartPosition));
			player.addEventListener('mediaStateChange', this.bind(this.onStateChange));
			
			// dispatch sound events
			obj.soundSlider.click(this.bind(this.onSoundChange));
			obj.setDefaultSoundValue(this.defaultSoundLevel);
			// end of sound level manipulations
			
			obj.timelineHolder.click(this.bind(this.mediaSeek));
		},		
		onPlayEvent : function(){
			// enable stop button
			this.stopButton.removeClass('mellowblue-stop-disabled');
			this.stopButton.addClass('mellowblue-stop');
	
			// change apperance of the play button
			this.playPauseButton.addClass('mellowblue-pause');
			this.playPauseButton.removeClass('mellowblue-play');
			this.playPauseButton.unbind("click").click(this.bind(this.pauseEvent));
		},
		
		onPauseEvent : function(){
			// change pause button to play
			this.playPauseButton.removeClass('mellowblue-pause');
			this.playPauseButton.addClass('mellowblue-play');	
			this.playPauseButton.unbind("click").click(this.bind(this.playEvent));
		},
					
		onStopEvent : function(){
			// disable stop button
			this.stopButton.addClass('mellowblue-stop-disabled');
			this.stopButton.removeClass('mellowblue-stop');
			
			// enable play button
			this.onPauseEvent();	
		},
	
		// pause event function			
		pauseEvent : function(){		
			this.player.sendMessage('uiPauseClick');				
		},
					
		// play event
		playEvent : function(){
			this.player.sendMessage('uiPlayClick');
		},
		
		// stop event
		stopEvent : function(){ 
			this.player.sendMessage('uiStopClick');	
		},
		
		// change volume (interface);
		changeVolume : function(){
			var slCoef = 100 / this.soundLevelMax;
			if($.browser.msie && Math.floor(parseFloat($.browser.version)) < 8){
				var bgOffset = soundLevel.css('backgroundPositionX') + ' ' + soundLevel.css('backgroundPositionY');
				soundLevel.css('backgroundPosition', bgOffset);
			}
			
			var currentSoundLevelArr = this.soundLevel.css('backgroundPosition').split(' ');
			if(currentSoundLevelArr.length != 2)return;
			var currentSoundLevel = 100 - Math.round(currentSoundLevelArr[0].replace('px', '') * -1 * slCoef);				
			this.player.sendMessage('soundValueSet', [currentSoundLevel]);				
		},
		
		// set default css offset by original sound level
		setDefaultSoundValue : function(level){
			var slCoef = 100 / this.soundLevelMax;				
			var cssLevel = this.soundLevelMax - Math.round(level / slCoef);
			this.soundLevel.css('backgroundPosition', (-1 * cssLevel) + 'px 0');
			this.changeVolume();
		},
		
		// function to handle change volume events
		onSoundChange : function(e){
			var xclick = e.clientX;
			var pos = this.soundLevel.offset();
			var changedVolume = xclick - pos.left;
			this.soundLevel.css('backgroundPosition', (-1 * (this.soundLevelMax - changedVolume)) + 'px 0');
			this.changeVolume();
		},
		
		
		/**
		 * timeline events
		 */
		// on media stop event change timeline position to 0 and
		// enable play button
		onMediaStopStartPosition : function(){
			this.changeTimelineSliderPosition(0);
			this.positionText.text(this.formatTime(0));		
			this.onPauseEvent();
		},
		// return formatted time
		formatTime : function(seconds){
			var s = seconds;
			var m = 0;
			if(isNaN(seconds))seconds = 0.1;
			if(seconds > 60){
				m = Math.floor(seconds / 60)
				s = seconds % 60;
			}
			return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
		},
		
					
		// change position of the timeline
		changeTimelineSliderPosition : function(pos){			
			var timelineCssOffset = this.timelineSlider.css('backgroundPosition');
			// fix jQuery bug in IE7
			if($.browser.msie && Math.floor(parseFloat($.browser.version)) < 8){
				timelineCssOffset = timelineSlider.css('backgroundPositionX') + ' ' + timelineSlider.css('backgroundPositionY');
				timelineSlider.css('backgroundPosition', timelineCssOffset);
			}
			var realPosition = (this.timelineOriginalLen - pos) * -1;
	
			this.timelineSlider.animate({
				'backgroundPosition' : '(' + realPosition + 'px ' + timelineCssOffset.split(' ')[1] + ')'
			}, 50);				
		},
		
		// show buffering state
		changeTimelineSliderBuffering : function(realSize, size, loaded){
			// if size and loaded is zero just skip change position of the buffering slider
			if(size == 0 && loaded == 0)return;
			// for HTTP Stream
			if(size != realSize){
				loaded = loaded + (realSize - size);
			}
	
			var coef = this.tLen / realSize;
			var tPos = Math.ceil(coef * loaded);
			var timelineCssOffset = this.timelineBuffering.css('backgroundPosition');
			// fix jQuery bug in IE7
			if($.browser.msie && Math.floor(parseFloat($.browser.version)) < 8){
				timelineCssOffset = timelineBuffering.css('backgroundPositionX') + ' ' + timelineBuffering.css('backgroundPositionY');
				timelineBuffering.css('backgroundPosition', timelineCssOffset);
			}
			var realPosition = (this.timelineOriginalLen - tPos) * -1;
			
			this.timelineBuffering.animate({
				'backgroundPosition' : '(' + realPosition + 'px ' + timelineCssOffset.split(' ')[1] + ')'
			}, 50);				
		},
		// state change
		onStateChange : function(s){
			this.duration = Math.ceil(s.duration);			
			var position = Math.ceil(s.position);
			var coef = this.tLen / this.duration;
			var tPos = Math.ceil(coef * position);
	
			this.durationText.text(this.formatTime(this.duration));
			this.positionText.text(this.formatTime(position));
			
			this.changeTimelineSliderBuffering(s.realbytestotal, s.bytestotal, s.bytesloaded);				
			this.changeTimelineSliderPosition(tPos);
		},
		// seek video position
		mediaSeek : function(e){
			var coef = this.tLen / this.duration;
			var posX = e.clientX;
			var offset = Math.ceil(posX - this.timelineHolder.offset().left);
			var position = Math.ceil(offset / coef);
			this.player.sendMessage('mediaSeek', [ position ]);
		}			
	};		
	
	
	//  ...
})(jQuery);
