/**
 * video.js
 * Description: YouTube chromeless player
 * Author: Ben Callaway
 */ 

function onYouTubePlayerReady(domID) {
	domID = unescape(domID);

	var player = document.getElementById(domID),
		video = { 
			// TODO This is stupid. Remember to find a better way to pass video id
			id: player.youTubeId,
			metaLoaded: false
		},
		$player = jQuery(player),
		$container = $player.parent().parent(),
		$play = $container.find('.play'),
		$overlay = $play.eq(0),
		$controls = $container.children('.controls'),
		$timeline = $controls.find('.timeline'),
		$duration = $timeline.find('.duration'),
		$loaded = $timeline.find('.loaded'),
		$played = $timeline.find('.played'),
		$scrubber = $timeline.find('.scrubber'), 
///		$cue = $timeline.find('.cue'),
		$elapsed = $controls.find('.elapsed'),
		$total = $controls.find('.total'),
///		$fullpage = $controls.find('.fullpage'),
		$sound = $controls.find('.sound'),
		$mute = $sound.find('.mute'),
		$volume = $sound.find('.volume'),
		
		// Classname constants
		READY_CLASS = 'ready',
		CUED_CLASS = 'cued',
		INITIATED_CLASS = 'initiated',
		PLAYING_CLASS = 'playing',
		MUTE_CLASS = 'mute',
		UPDATING_CLASS = 'updating',

		// Cookie constants and defaults
		VOLUME_COOKIE_NAME = 'TMIvolume',
		DEFAULT_VOLUME = 66,
///		QUALITY_COOKIE_NAME = 'TMIVideoQuality',
		DEFAULT_QUALITY = 'default',
		OVERLAY_OPACITY_CSS = {
			'-ms-filter': $overlay.css('-ms-filter'),
			'filter': $overlay.css('filter'),
			'opacity': $overlay.css('overlay')
		},
		OVERLAY_ANIMATION_DURATION = 500,

		// Player states
		UNSTARTED = -1,
		ENDED = 0,
		PLAYING = 1,
		PAUSED = 2,
		BUFFERING = 3,
		VIDEO_CUED = 5,
		
		// Error codes			
		NOT_FOUND = 100,
		EMBEDDING_NOT_ALLOWED_1 = 101,
		EMBEDDING_NOT_ALLOWED_2 = 150,		

		// Object for holding interface refresh functions 
		updater;
	
	// YouTube & DOM interface functions
	function play(e) {
		player.playVideo();
	}

	function pause(e) {
		player.pauseVideo();
	}

	function stop(e) {
		player.stopVideo();
	}
	
	function seek(seconds) {
		// Save a copy of player state before seeking to new position
		var paused = (video.state === PAUSED) || (video.state === ENDED);

		player.seekTo(seconds, true);
		
		if (paused) {
			pause();
		}
	}
	
	function mute(e) {
		player.mute();
		$sound.addClass(MUTE_CLASS);
	}
	
	function unmute(e) {
		player.unMute();
		$sound.removeClass(MUTE_CLASS);
	}
	
	// Helper functions for player updates/refreshes
	function setUpdater(name, time) {
		if (typeof updater.interval[name] === 'undefined') {
			updater.interval[name] = setInterval(updater[name], time);
		}
	}

	function clearUpdater(name) {
		updater.interval[name] = clearInterval(updater.interval[name]);
	}

	function initUpdaters() {
		var name, time;
		
		for (var i=0; i<updater.length; i++) {
			name = updater[i];
			time = (name === 'elapsed') ? 1000 : 50 ;
			setUpdater(name, time);
		}
	}

	function killUpdaters(afterThisCycle) {
		for (var i in updater.interval) {
			if (updater.hasOwnProperty(i)) {
				if (afterThisCycle) {
					updater[i]();
				}
				clearUpdater(i);
			}
		}
	}

	// Misc helper functions
	function format(time) {
		var min = Math.floor(time / 60),
			sec = Math.round(time % 60),
			pad = function (n) { 
				return (n > 9) ? n : '0' + n ;
			};

		return [pad(min), ':', pad(sec)].join('');
	}

	function extendEventScope(context, bindings, unbinder) {
		var $context = jQuery(context),
			namespace = '.eventExtension' + parseInt(Math.random() * 10e6, 10),
			type, thisBinding, handler;

		for (var i=0; i<bindings.length; i++) {
			for (var n in bindings[i]) {
				if (bindings[i].hasOwnProperty(n)) {
					thisBinding = bindings[i];					
					type = n + namespace;
					handler = thisBinding[n];
					$context.bind(type, handler);
				}
			}
		}

		if (unbinder) {
			$context.bind(unbinder, function () { $context.unbind(namespace); });
		}
	}		

	function setWithinRange(n, min, max) {
		return (n < min) ? min : (n > max) ? max : n ;
	}

	function scrubTo(position) {
		var cuePoint;

		position = setWithinRange(position, $duration.min, $duration.max);

		$scrubber.css({ left:position });
		cuePoint = position / $duration.max * video.duration;

		return cuePoint;	
	}

	function slideScrubber(e) {
		var $this = jQuery(this),
			position, cue;

		function refresh(e) {
			position = e.pageX - $scrubber.positionReference - $scrubber.midWidth;
			cue = scrubTo(position);
		}

		// prevent browser's drag/drop functionality
		e.preventDefault();

		// cancel auto updating of scrubber position
		clearUpdater('scrubber');

		$this.addClass(UPDATING_CLASS);

		refresh(e);

		extendEventScope('body', [{ mousemove:refresh }, { mouseup:function () { seek(cue); $this.removeClass(UPDATING_CLASS); } } ], 'mouseup');
	}

	// 'level' takes a value b/w 0 and 100
	function setVolume(level) {
		level = parseInt(level, 10);
		level = setWithinRange(level, 0, 100);
		$volume.width(level + '%');
		if (player.isMuted()) {
			unmute();
		}
		player.setVolume(level);
		TMI.writeCookie(VOLUME_COOKIE_NAME, level);
	}

	function slideVolume(e) {
		var $this = jQuery(this),
			level;

		function refresh(e) {
			level = (e.pageX - $volume.offset().left) / $volume.max * 100;
			setVolume(level);
		}
		
		e.preventDefault();

		$this.addClass(UPDATING_CLASS);

		refresh(e);
		
		extendEventScope('body', [{ mousemove:refresh }, { mouseup:function () { $this.removeClass(UPDATING_CLASS); } } ], 'mouseup');
	}

	// TODO can be done server-side
	function populateEmbedCode() {
		jQuery('input#embed_code').val(player.getVideoEmbedCode());
	}

	function initVolume() {
		var level = TMI.readCookie(VOLUME_COOKIE_NAME);

		if (player.isMuted()) {
			mute();
		} else {
			level = (level !== null) ? parseInt(level, 10) : DEFAULT_VOLUME;
			setVolume(level);
		}		
	}

	// Get meta data and initialize 'playing' interface
	function initVideo() {
		video.duration = player.getDuration();
		video.bytes = {
			stert: player.getVideoStartBytes(),
			loaded: player.getVideoBytesLoaded(),
			total: player.getVideoBytesTotal()
		};

		if (video.bytes.total > 0) {
			video.metaLoaded = true;
			$total.html(format(video.duration));
			initUpdaters();
		}
		
		setTimeout(function () {$container.addClass(INITIATED_CLASS); }, OVERLAY_ANIMATION_DURATION);
	}

	 // YouTube events -- (handlers are required to be globally defined)
	function attachYouTubeListeners() {
		var listeners = {
			onStateChange: function (state) {

				video.state = state;

				switch (state) {
					case UNSTARTED:
						$play
							.unbind()
							.click(play)
							.removeClass(PLAYING_CLASS);							

						break;
					case ENDED:
						$play
							.unbind()
							.click(play)
							.removeClass(PLAYING_CLASS);

						$overlay
							.stop()
							.css(OVERLAY_OPACITY_CSS);

						killUpdaters(true);
						break;
					case PLAYING:
						$play
							.unbind()
							.click(pause)
							.addClass(PLAYING_CLASS);							

						$overlay.animate({ opacity:0 }, OVERLAY_ANIMATION_DURATION);
						if (video.metaLoaded === false) {
							initVideo();
						} else {
							initUpdaters();
						}

						video.duration = player.getDuration();
						$total.html(format(video.duration));

						break;
					case PAUSED:
						$play
							.unbind()
							.click(play)
							.removeClass(PLAYING_CLASS);							

						$overlay
							.stop()
							.css(OVERLAY_OPACITY_CSS);
							
						killUpdaters();
						break;
					case BUFFERING:
						break;
					case VIDEO_CUED:
						$play
							.unbind()
							.click(play)
							.removeClass(PLAYING_CLASS);
						
						$loaded.width(0)
						$played.width(0)
						$scrubber.css('left','0')

						$total.text('00:00')

						$container.addClass(CUED_CLASS);
						break;
				}
			},
		
			onPlaybackQualityChange: function (quality) {
				// playback quality stuff
			},
		
			onError: function (code) {
				switch (code) {
					case NOT_FOUND:
						break;
					case EMBEDDING_NOT_ALLOWED_1:
						break;
					case EMBEDDING_NOT_ALLOWED_2:
						break;
				}
			}
		},
		handlerName;
	
		for (var event in listeners) {
			if (listeners.hasOwnProperty(event)) {
				// Remove any characters that will break External Interface
				handlerName = domID.replace(/[.\-+\*\/\\]/g,'_') + event;
				window[handlerName] = listeners[event];
				player.addEventListener(event, handlerName);
			}
		}
	}

	/////////////////////////////////
	// Initialize player interface //
	/////////////////////////////////

	$timeline
		.mousedown(slideScrubber);

	$mute
		.toggle(mute, unmute)
		.mousedown(function (e) {
			e.stopPropagation();
		});

	$sound
		.mousedown(slideVolume);

	// Create updater object for interval refreshes of GUI
	// Updaters fire in the order specified by the integer keys
	updater = {
		player: function () {
			video.bytes.loaded = player.getVideoBytesLoaded();
			video.time = player.getCurrentTime();
			$loaded.progress = video.bytes.loaded / video.bytes.total * $duration.max;
			$loaded.width($loaded.progress);
			$played.progress = video.time / video.duration * $duration.max;
			$played.width($played.progress);
		},
		scrubber: function () {
			scrubTo($played.progress);
		},
		elapsed: function () {
			$elapsed.text(format(video.time));
		},
		interval: {},
		0: 'player',
		1: 'scrubber',
		2: 'elapsed',
		length: 3
	};
	
	attachYouTubeListeners();

	// Record dimensions and positions of interface elements
	$duration.min = 0;
	$duration.max = parseInt($duration.width(), 10);
	$volume.min = 0;
	$volume.max = parseInt($sound.find('.max').width(), 10);
	$scrubber.positionReference = $timeline.offset().left;
	$scrubber.midWidth = parseInt($scrubber.width(), 10) / 2;

	player.cueVideoById(video.id, 0, DEFAULT_QUALITY);
	
	initVolume();

	populateEmbedCode();

	$container.addClass(READY_CLASS);

	jQuery('.youtube-list li').click(function(){
		if(jQuery(this).hasClass('.current'))
			return false

		jQuery('.youtube-list li').removeClass('current')

		jQuery(this).addClass('current')

		curr_url = jQuery(this).find('a').attr('href')
			
		curr_results = curr_url.match("[\\?&]v=([^&#]*)");
	    curr_vid = curr_results[1]

		player.cueVideoById(curr_vid, parseInt(0), undefined)
	
	})
}
