(function(){
    $=jQuery;
    var imdb = window.imdb = this.imdb = {};
	if (!imdb) imdb={};
	if (!imdb.tv) imdb.tv = {};
	if (!imdb.tv.widget) {
		imdb.tv.widget = {
			cache:{},
			context:null,
			content_node:null,
			is_dynamic:null,
			show_episode:null,
			panel_queue:[]
		};
	}
	/**
	* Construct a redirect to record an action
	* @param {String} identify this action
	*/
	function get_rg_tag(slot, target){
		return "/rg/" + get_context() 
			+ "/" + slot 
			+ "/" + target;
	}
	/**
	* Fire an rg pixel to record this action
	* @param {String} identify this action
	*/
	function load_rg(slot){
		var img = new Image();
		img.src = get_rg_tag(slot, "http://i.media-imdb.com/b.gif");
	}
	/**
	* Break down a string and get date and time 
	* @param {String} format: /yy-mm-dd/hh:mm (AM|PM)
	* @return {Object} {date:<date>, time:<time>}
	*/
	function parse_datetime(ref){
		var tokens = ref.split(/\//);
		return { date: tokens[0], time: tokens[1], pretty_date: tokens[2] };
	}
	/**
	* Attempt to update any class=tv_pretty_date objects
	*/
	function update_pretty_date(){
		var datetime = parse_datetime(
			$('#grid_start').attr('ref')
		);
		var $target = $('#'+get_context()+' .tv_pretty_date');
		if ($target.length > 0){
			$target[0].innerHTML = datetime.pretty_date;
		}
	}
	/**
	* Searches for an input w/ id=timezone_select
	* @return {String} timezone 
	*/
	function get_timezone(){
		var $target = $('#tv_timezone_select');
		if ($target.length == 0) return null;
		return encodeURI($target.attr('value'));
	}
	/**
	* Searches for an element with class=tv_widget
	* and grabs the tv_widget's context value
	* @returns {String} the element's id
	*/
	function get_context(){
		if (imdb.tv.widget.context == null) {
			var $target = $('.tv_widget');
			if ($target.length > 0){
				imdb.tv.widget.context = $target[0].id;
			}
			else {
				imdb.tv.widget.context = "";
			}
		}
		return imdb.tv.widget.context;
	}
	/**
	* Searches for a class=tv_content element inside 
	* of a class_tv_widget element where grid's should be rendered
	* @returns {Element}
	*/
	function get_dock_node(){
		if (imdb.tv.widget.content_node == null) {
			var $target = $('#'+get_context()+' .tv_dock');
			if ($target.length > 0){
				imdb.tv.widget.content_node = $target[0];
			}
		}
		return imdb.tv.widget.content_node;
	}
	/**
	* Determines if this tv widget should be loaded dynamically
	* @returns {Boolean}
	*/
	function is_dynamic(){
		if (imdb.tv.widget.is_dynamic == null) {
			var $target = $(get_dock_node());
			imdb.tv.widget.is_dynamic = $target.hasClass('dynamic');
		}
		return imdb.tv.widget.is_dynamic;	
	}
	/**
	* Determines if episode data should be shown
	* @returns {Boolean}
	*/
	function show_episode(){
		if (imdb.tv.widget.show_episode == null) {
			var $target = $(get_dock_node());
			imdb.tv.widget.show_episode = $target.hasClass('show_episode');
		}
		return imdb.tv.widget.show_episode;	
	}
	/**
	* Turns on the spinner to signal data loading inprogress
	*/
	function activate_spinner(){
		$(get_dock_node()).addClass('tv_loading');
	}
	/**
	* Turn off the spinner to signal data loading complete
	*/
	function deactivate_spinner(){
		$(get_dock_node()).removeClass('tv_loading');
	}
	/**
	* Hides all open tv_info panels
	*/
	function close_panels(){
		while(imdb.tv.widget.panel_queue.length > 0){
			var show_id = imdb.tv.widget.panel_queue.pop();
			//hide tv_info
			$("#"+show_id+"_info").css('display', 'none');
			//un-highlight the tv_show
			$("#"+show_id+"_show").removeClass('active');
		}
	}
	/**
	* Open up a specific tv_info panel
	* @param {event} the click (or mouseover) event that requested this
	* @returns {Boolean} successfully opened the panel from a closed state
	*/
	function open_panel(e){
		var $target = $(e.target);
		var show_id = $target.attr('ref');
		var $tv_info = $("#"+show_id+"_info");
		var $tv_show = $("#"+show_id+"_show");
		if ($tv_info.css('display') == 'none') {
			//close all other panels
			close_panels();
			//enque this panel's id
			imdb.tv.widget.panel_queue.push(show_id);
			//reposition panel
			$tv_info.css('left',$target.offset().left - $tv_info.width() / 2);
			$tv_info.css('top',$target.offset().top + $target.height());
			//display tv_info
			$tv_info.css('display', 'block');
			//highlight the tv_show
			$tv_show.addClass('active');
			
			$tv_info.find(".tv_info_inner")
			.unbind()
			.bind("mouseleave click", function(e){
				close_panels();
			});
			return true;
		}
		return false;
	}
	/**
	* Uses timezone and time/date info as a key to save ajax requested grid
	*/
	function cache_state(){
		var $target = $(".tv_dock");
		if ($target.length == 0) return false;
		var datetime = parse_datetime(
			$('#grid_start').attr('ref')
		);

		var key = get_timezone() + datetime.date + datetime.time;
		imdb.tv.widget.cache[key] = $target[0].innerHTML;;	
	}
	/**
	* Acquires a previously requested tv grid state
	* @param {String} timezone value
	* @param {String} date value
	* @param {String} time value
	* @return {String} stored html for an old tv grid
	*/	
	function get_state(timezone, date, time){
		var key = timezone + date + time;
		return imdb.tv.widget.cache[key];
	}
	/**
	* Make an ajax call (or use a cached value) to get and install tv grid html
	* @param {String} timezone value
	* @param {String} date value
	* @param {String} time value
	* @param {Boolean} do not use cache values (force an ajax request)
	* @return {Boolean} load successful
	*/
	imdb.tv.widget.load = function (timezone, date, time, ignoreCache) {
		var context = get_context();
		var $target = $("#"+context+" .tv_dock");
		if ($target.length == 0) return false;
		
		activate_spinner();
		
		var path = '/tv/widget/grid';
		var params = [];
		if (context)
			params.push("context="+context);
		if (timezone) 
			params.push("timezone="+timezone);
		if (date) 
			params.push("start_date="+date);
		if (time) 
			params.push("start_time="+time);
		if (show_episode())
			params.push("show_episode=1");
		
		var request = path + "?" + params.join('&');
		
		var old_state = get_state(timezone, date, time);
		if (old_state && !ignoreCache) {
			$target[0].innerHTML = old_state;
			imdb.tv.widget.init();
		}
		else {
			$target.load(request, null, function(){
				imdb.tv.widget.init();
			});
			//TODO: handle a load failure
		}
		return true;
	};

	/**
	* Add the dynamic functionality to the newly created tv grid html
	* @param {String} jquery selector to target just a part of a tv grid e.g. "#row_1"
	*/
	imdb.tv.widget.init  = function (selector) {
		if (selector == null){
			selector = '#'+get_context();
		}
		update_pretty_date();
		
		// Setup the tv_info panels
		$(selector + ' .tv_info_toggle')
		.unbind()
		.bind("mouseenter", function(e){
			open_panel(e);
		});
		// Rewrite URLs to track clicks
		$(selector + ' a')
		.unbind()
		.click(function(e){
			e.preventDefault();
			var $target = $(e.target);
			var href = $target.attr('href');
			if (e.target.nodeName !== 'A') {
				// clicked target is not an anchor (i.e., it is the
				// image element in '<a href="/foo"><img></a>') so
				// we seek the closest parent anchor element
				var parent_anchors = $target.parents('a');
				if (parent_anchors.length >= 1) {
					// there is at least one parent anchor element,
					// so pull href of the closest one
					href = $(parent_anchors[0]).attr('href');
				}
				// else {
				//     this shouldn't happen
				// }
				
				// NOTE: in jQuery 1.3 the above becomes simply:
				// 
				//   $target.closest('a').attr('href')
			}
			// else {
			//   // clicked target is A
			// }
			if (href) { 
				window.location = get_rg_tag("click_link",href);
				return false;
			}
			// if href is not defined, then nothing will happen
			// (.unbind() earlier removed the href, so returning
			// true will not trigger the link to be followed.)
		});
		// Setup the ajax functionality for timezone and fwd/rev nav buttons
		if (is_dynamic()){
			cache_state();
			
			$(selector + ' #tv_timezone_select')
			.unbind()
			.change(function(e){
				var datetime = parse_datetime(
					$('#grid_start').attr('ref')
				);		   
				imdb.tv.widget.load(get_timezone(), 
					datetime.date, datetime.time, true);
				load_rg("timezone_select")
			});
			
			$(selector + ' .tv_rev a, '+selector + ' .tv_fwd a')
			.unbind()
			.click(function(e){
				e.preventDefault();
				var datetime = parse_datetime(
					$(e.target).attr('ref')
				);
				imdb.tv.widget.load(get_timezone(), 
					datetime.date, datetime.time);
				load_rg("scroll_nav")
			});
			
		}
		
		deactivate_spinner();
	};
	
	// Shortcut
	window['imdb_tv_widget_init'] = imdb.tv.widget.init;

	// When the document has loaded setup the grid
	$(document).ready( 
		function(){
			if (is_dynamic()){
				imdb.tv.widget.load();
				return;
			}
		}
	);
})();


