/**
 * bam.media.vpp: App core for the Video Playback Page and supporting objects 
 * (included here until bundling is ready)
 * @author Aleksandar Kolundzija
 * @version 4.0a
 *
 * @TODO Consider preventing commenting during preroll (via adPlaybackStart and adPlaybackEnd)
 */


/**
 * Loggable adds a log method to the passed object.
 * @param obj       {Object}  Object which will get a new log() method
 * @param objName   {String}  Optional parameter for displaying a string before each log output
 * @param debugMode {boolean} Optional switch for disabling logging
 */
 // @TODO: prefix is in the global scope. Why?
$.loggable = function(obj /* , objName, debugMode */){
	var objName   = arguments[1] || "",
			debugMode = (typeof arguments[2]!=="undefined") ? arguments[2] : true;
			prefix    = objName ? objName + ": " : "";
	obj.log = (function(prefix){
		return function(){			
			if (debugMode && typeof console!=="undefined"){
				if (arguments.length){
					arguments[0] = prefix + arguments[0];
				}
				// console.log.apply(null, arguments);
				console.log.apply(console, arguments);
			}			
		}
	})(prefix);
	return obj;
};
	
	
/**
 * ClipDataManager retrieves and renders clip data
 */
bam.media.ClipDataManager = function(cfg){
    bam.loadSync(bam.homePath + "bam.datetime.js");
    bam.loadSync(bam.homePath + "bam.social.js");
    
	
	var _cfg = {
				disableComments : false,
				enableTagLinks	: true,  // flag for hyperlinking tags
				psKeys          : ["FLASH_1200K_640X360", "FLASH_1000K_640X360", "FLASH_800K_640X360", "FLASH_800K_512X288", 
													 "MLB_FLASH_800K_STREAM_VPP", "MLB_FLASH_800K_STREAM", 
													 "MLB_FLASH_1000K_PROGDNLD", "MLB_FLASH_800K_PROGDNLD", 
													 "v2_url"],
				prodPageUrl     : "http://mlb.mlb.com/video/play.jsp", // used for Facebook link
				shareVideoUrl   : "/media/email/send.jsp",
				club            : "", // used for generating complete share url
				topicId         : "", // used for generating complete share url
				searchPageUrl   : "/search/media.jsp",
				keywordTaxTypes : { // @TODO set these per property externally
					"mlbtax"    : "mlbtax_key",
					"yes_tax"   : "yes_tax_key",
					"tiger_tax" : "tiger_tax_key",
					"mlstax"    : "mlstax_key",
					"sny_tax"   : "sny_tax_key",
					"ventax"	: "ventax_key",
					"gbtax"		: "gbtax"					
				},
				debugMode : false
			},
            
			_curContentId = "",
			_clips        = {}; // private hash of known clips and their data

	$.extend(_cfg, cfg);
	

	var _self = {


		/**
		 * Converts date strings from YYYY-MM-DDTHH:MM:SS-???? to MM/DD/YY
		 */
		formatDate: function(dateLong){
			return dateLong.replace(/^\d{2}(\d{2})-(\d{2})-(\d{2}).+/, "$2/$3/$1");
		},
			
		
		/**
		 * Converts duration strings HH:MM:SS as follows:
		 * 11:59:00 -> 11:59:00
		 * 01:59:00 -> 1:59:00
		 * 00:59:00 -> 59:00
		 * 00:00:30 -> 00:30	
		 */
		formatDuration: function(duration){
  			var arr= duration.split(":"),	
      			hours = +arr.shift();
    		if(hours*1) arr.unshift(hours);
			return arr && arr.join(":") || function(){
				$("#clipDate").css("border","none");
				return "";
			}();
		},	
				
		/**
		 * Gets clip data (formatted)
		 */		
		getClipData: function(contentId){
		
			_self.log("getClipData: contentId=" + contentId);		
		
			if (_clips[contentId]){
				return _clips[contentId]; // use memoized
			}
			var clip = bam.media.getMetaData(contentId);
			if (clip && typeof clip.urls!=="undefined"){

				var urlNode = {},
                    psMap   = {},
                    embedRules = {};

				$.each(clip.urls, function(i, url){ // load available playback scenarios into a hash for easy reference
					if (url.getAttribute("playback_scenario")){
						psMap[url.getAttribute("playback_scenario")] = url;
					}
					else if (url.getAttribute("speed")=="800" && url.getAttribute("type")=="flash-video"){
						psMap.v2_url = url;
					}
				});
				
				
                $.each(clip.keywords, function(i, keyword) {
                    var value = keyword.getAttribute("value");					
                    // set flags for video as being embeddable or not embeddable. 
                    // uncomment and update the value of not_embeddable once the correct keyword value is known
                    if (value === "embeddable" || value === "not_embeddable") {
                        embedRules[value] = true;
                    }
                });

				$.each(_cfg.psKeys, function(i, psKey){
					if (psMap[psKey]){
						urlNode = psMap[psKey]; // get the first match and exit
						return false;
					}
				});
				if (urlNode && urlNode.firstChild && urlNode.firstChild.nodeValue){
                    
                    clip.embedRules = embedRules;
                    clip.publishDateTime = bam.datetime.parseXMLDate(clip.date);
					clip.curVideoFlashUrl = urlNode.firstChild.nodeValue;
					clip.curVideoFlashId  = urlNode.getAttribute("id");
					clip.playbackScenario = urlNode.getAttribute("playback_scenario");
					var bitRateArr = clip.playbackScenario.match(/(\d+K)/);
					clip.bitRate   = (bitRateArr) ? bitRateArr[1] : "";
					_self.log("getClipData: Found URL [" + clip.playbackScenario + "] URL: " + clip.curVideoFlashUrl);
				}
				else {
					_self.log("getClipData: PlaybackScenario (or v2 data) is missing.");
					return false;
				}
			}
			else {
				_self.log("getClipData: Meta data is not available or bad. Returning false.");
				return false;
			}
			_clips[contentId] = clip;
			return clip;
		},
		

		/**
		 * Displays clip info on page
		 * @TODO consider moving messages into a private hash
		 * 
		 * Embed button rules:
		 * 
		 * For MLB:
		 * 	- The threshold for embedding is 48 hours.
		 * 	- The default setting is to show the embed button. We will hide the embed button if any of these conditions are met: 
		 * 		- The embeddable tag is "on" and the clip publish date is outside the threshold
		 * 		- The not_embeddable tag is "off"
		 * 
		 * For non-MLB:
		 * 	- The threshold for embedding is 0 hours.
		 * 	- The default setting is to not show the embed button. We will show the embed button if this condition is met: 
		 * 		- The embeddable tag is "on" and the not_embeddable tag is "off" (or does not exist)
		 * 
		 */
		showClipInfo: function(clip){
            var embedThreshhold = (_cfg.property === "mlb") ? 48 : 0, // approximate hours of embed threshold
            shareObj,
            hoursSincePublish;
			
			if ( 
					(typeof clip.content_id==="undefined") || 
					(typeof clip.curVideoFlashUrl==="undefined") 
					){ // bad data. handle display and exit
				_self.log("bad data. (content_id or URL undefined)");
				$("#clipTitle").text("This video is temporarily unavailable");
				$("#clipBlurb").text("Please make another selection.");
				$("#clipDate span,#clipDuration").text("n/a");
				$("#clipTags div").html("");
				$("#clipShare a").unbind("click");
				return false;
			}
			
			// data is ok
			_curContentId = clip.content_id;
			_self.log("data is OK. rendering content id... " + _curContentId);
			$("#clipTitle").text(clip.headline);
			$("#clipDate span").text(_self.formatDate(clip.date));
			$("#clipDuration").text(_self.formatDuration(clip.duration));
			$("#clipThumb").html("<img src='"+clip.thumb+"'/>");
			$("#clipBlurb").text(clip.bigBlurb);
			_self.loadKeywords(clip);
			
			// create a social media object.

			shareObj = new bam.social.Lib({
			  title      : clip.headline,
			  url        : getShareURL(),
			  description: clip.bigBlurb,
			  sequenceloading : false
			}).twitter("#twitter a", {
               tracking: {tcid: 'tw_video' + "_"+_curContentId},
               shorty  : $("body").hasClass("MiLB") ? false : true,
               omniture: {async:{
                                   "compName":"Video Playback Distribution/Comments",
                                   "compActivity":"Video Playback Twitter Share Click",
                                   "actionGen":true,
                                   "club": _curContentId
                                   }}
            }).facebook("#facebook", {
               tracking: {tcid: 'fb_video' + "_"+_curContentId},
               track_pfx: "Holliday Inn Sweeps: Landing Page: Like Button "
            }).google("#googleplusone", {
               annotation: "none",
               showCounters : true,
               size : "standard",
               omniture: {async:{
                                   "compName":"Video Playback Distribution/Comments",
                                   "compActivity":"Video Playback Google Plus Share Click",
                                   "actionGen":true,
                                   "club": _curContentId
                                   }}

            });
            			 
            			 
			


			
			if (!_cfg.disableComments){
				// enable/disable comments based on data
				// @TODO - move this to the core app?
				$("#comment").find("a").html( clip.commentsAreClosed ? "<span class='closed'>Comments closed</span>" : "Comment" );
			}

			
			// Embed button logic
			
            hoursSincePublish = bam.datetime.DateDiff(_cfg.currentTime, clip.publishDateTime).TotalHours();
			
			_self.log("***** Embed data for " + _cfg.property + ": *****");
			_self.log("hoursSincePublish="+hoursSincePublish );
			_self.log("embedThreshhold="+embedThreshhold );
			_self.log("embeddable="+clip.embedRules.embeddable );
			_self.log("not_embeddable="+clip.embedRules.not_embeddable );			
			
			if(_cfg.property === "mlb"){
			
				// hide the embed button if the clip is embeddable and has been published inside the threshhold
          	  	// also, always hide the clip if the not embeddable flag is set
            	if ( (!clip.embedRules.embeddable && hoursSincePublish <= embedThreshhold) || (clip.embedRules.not_embeddable)) {
                	$("#btn-share-embed").hide();   
					_self.log("hide embed");
            	} else {
                	$("#btn-share-embed").show();   
					_self.log("show embed");
            	}
			
			}else{
			
				// show the embed button if the clip is embeddable and the not embeddable flag is not set
          	  	// embedThreshhold=0 here, but I left the condition in case that changes
            	if ( (clip.embedRules.embeddable && hoursSincePublish >= embedThreshhold && !clip.embedRules.not_embeddable)) {
                	$("#btn-share-embed").show();   
					_self.log("show embed");
            	} else {
                	$("#btn-share-embed").hide();   
					_self.log("hide embed");
            	}

			}			
            
                        
			return true;
		},
		

		/**
		 * Generates keyword list and appends it to DOM. Augments passed object with commentsAreClosed property.
		 * @param {object} clip
		 */			
		loadKeywords: function(clip){
			_self.log("loading keywords");
			clip.commentsAreClosed = false; // custom keyword for disabling commenting
			if ($("#clipTags").length){ // if elem #clipTags exists @TODO Why wouldn't it?
				var type, display, value, param, 
						keywordsHtml = "";
				$("#clipTags div").empty();
							
				$.each(clip.keywords, function(i, keyword){
					type = keyword.getAttribute("type");
					
					
					if (_cfg.keywordTaxTypes[type]){
						param = _cfg.keywordTaxTypes[type];						
						type  = "tax";
					}
					
				
					switch (type){
						case "tax" : 
							display = keyword.getAttribute("displayName").replace(/\"/g,"");
							// if ( !(/^[A-Z_0-9]*$/.test(display)) ){ // Ignore keywords made up of caps and numbers (mobile only)							
							if( !(/^\d+$/.test(display)) ) { // Ignore keywords made up numbers (mobile only)								
								value = keyword.getAttribute("value");
								// filter "embeddable" and "shareable" out
								if(~value.indexOf("shareable") || ~value.indexOf("embeddable")) value = null;
							}
							break;
						case "team_id"       :
						case "mlb_team_id"   :
						case "player_id"     :
						case "mlb_player_id" :
							param   = type;
							value   = keyword.getAttribute("value");
							display = keyword.getAttribute("displayName");
							break;
						case "game_pk" :
							param   = "game_pk";
							value   = keyword.getAttribute("value");
							display = "More From This Game";
							break;
						/**
						* Hack for hiding the comments when video doesn't support them.
						* Its here, rather than in showClipInfo() since we don't want
						* to iterate keywords twice.
						*/
						case "mmtax" :
							if (keyword.getAttribute("value")==="comments_closed"){
								clip.commentsAreClosed = true;
							}
							break;
					}
					
					
					if (param && value && display){
						if(_cfg.enableTagLinks) {
							keywordsHtml += "<a href='"+_cfg.searchPageUrl+"?" + param+"="+escape(value) + (_cfg.skinParam ? _cfg.skinParam : "") + "'>"+ display +"</a>, ";
						} else {
							keywordsHtml += display + ", ";
						}											
					}
					param = type = display = value = null; // reset
				});
				if (keywordsHtml.length > 0){
					$("#clipTags div").html(keywordsHtml.substring(0, keywordsHtml.length-2)); // remove trailing comma and space
				}
			}
		},
		

		/**
		 * Launches the Email Link popup.
		 */
		shareVideo: function(){
			var clip   = _clips[_curContentId],
				url    = _cfg.shareVideoUrl+"?content_id="+clip.content_id+"&metaId="+clip.content_id+"&title="+escape(clip.headline)+"&blurb="+escape(clip.bigBlurb) + (_cfg.skinParam ? _cfg.skinParam : ""),
					width  = 500,
					height = 470;
			if (width > screen.availWidth-12){ width = screen.availWidth-12; }
			if (height > screen.availHeight-48){ height = screen.availHeight-48; }
			var left = (screen.availWidth - width - 12) / 2;
			var top  = (screen.availHeight - height - 48) / 2;
			window.open(url, name, "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
		}		
		
	};

    /**
     * Returns fully built share URL with tracking code 
     *
     * @param {String} trackingCode tcid param value
     * @return {String}
     */
    function getShareURL (trackingCode) {
      return _cfg.prodPageUrl + "?content_id=" + _curContentId + "&topic_id=" + _cfg.topicId + ((_cfg.property==="mlb") ? "&c_id=" + _cfg.club : "") + (_cfg.skinParam ? _cfg.skinParam : "") + ((trackingCode) ? "&tcid=" + trackingCode + "_"+_curContentId : "") + "&v=3"; // "v" param used for breaking facebook cache
    }
    
   
    /**
     * Returns HTML for textarea tag that contains the object embed code
     * for the currently playing video item
     *
     * @return {String}
     */
    function getEmbedHTML() {
		var width = 400,
            height = 224,
            html = [];
		/*
		// object embed:
		html.push('<object width="', width, '" height="', height, '">');
       		html.push('<param name="movie" value="' + _cfg.baseUrl + '/shared/flash/video/share/ObjectEmbedFrame.swf?content_id=', _curContentId, '&topic_id=', _cfg.topicId, '&width=', width, '&height=', height, '&property=' + _cfg.property + '"', ' />');
           	html.push('<param name="allowFullScreen" value="true" />');
           	html.push('<param name="allowscriptaccess" value="always" />');
       		html.push('<param name="scale" value="noscale" />');
           	html.push('<param name="salign" value="tl" />'); 
           	html.push('<embed src="' + _cfg.baseUrl + '/shared/flash/video/share/ObjectEmbedFrame.swf?content_id=', _curContentId, '&topic_id=', _cfg.topicId, '&width=', width, '&height=', height, '&property=' + _cfg.property + '"');
           	html.push(' type="application/x-shockwave-flash"'); 
           	html.push(' allowscriptaccess="never" ');
           	html.push(' allowfullscreen="true" ');
           	html.push(' width="', width, '"');
           	html.push(' height="', height, '"');
           	html.push(' scale="noscale"');
           	html.push(' salign ="tl" />');
       	html.push('</object>');
		*/	
		// iframe embed:
		html.push("<iframe src='" + _cfg.baseUrl + "/shared/video/embed/embed.html?content_id=" + _curContentId + "&width=" + width + "&height=" + height + "&property=" + _cfg.property + "' width='" + width + "' height='" + height + "' frameborder='0'>");
		html.push("Your browser does not support iframes.");	
		html.push("</iframe>");	
		return "<div class='embed-container'><p>Copy and paste to embed this video:</p><textarea class='embed-code'>" + html.join("") + "</textarea></div>";	
    }
	
	
	// tag clicks
	$("#clipTags div a").live("click", function(e){
		_self.trigger("tagClick");
	});			
	
	// comments expand/collapse click
	$("#comment").live("click", function(e){
		e.preventDefault();
		if (!_clips[_curContentId].commentsAreClosed){
			_self.trigger("commentButtonClick");
		}
	});	
	
	// set share link
	$("#share").live("click", function(){
		_self.trigger("shareClick", ["Email Video"]);
		_self.shareVideo();
	});
			
	// set facebook link
	$("#facebook").live("click", function(){
		_self.trigger("shareClick", ["Facebook"]);		
	});
			
	// set twitter link
	$("#twitter").live("click", function(){
		_self.trigger("shareClick", ["Twitter"]);		
	});
		
	// set digg link
	$("#digg").live("click", function(){
		_self.trigger("shareClick", ["Digg"]);
		var url = getShareURL("dg_video");
		diggUrl = "http://digg.com/submit?url=" + encodeURIComponent(url)
						+ "&title="+encodeURIComponent(_clips[_curContentId].headline)
						+ "&bodytext="+encodeURIComponent(_clips[_curContentId].bigBlurb);
		window.open(diggUrl,"vppsharedigg");
	});

    // copy link
    $("#btn-share-copy").live("click", function() {
        if ( ! bam.popModule) {
            bam.loadSync(bam.homePath + "bam.popModule.js");
            bam.popModule.init({
                css : "/shared/components/gameday/v6/css/gda-login-popModule.css"    
            });
        }

		_self.trigger("shareClick", ["Copy"]);

		var url = getShareURL("vpp_copy");

        bam.popModule.show({
            htmlContent : "<div class='embed-container'><p>Link to this video:</p><textarea class='copy-code'>" + url + "</textarea></div>",
            postShow : function() {
                textarea = $("#module_content").find("textarea");
                textarea.click(function() {
                    this.focus();
                    this.select();
                });
            }
        });

        bam.popModule.postExit = function() {
            if (textarea) { 
                textarea.unbind();
            }
        };

        return false;

    });

    // embed 
    $("#btn-share-embed").live("click", function() {
        if ( ! bam.popModule) {
            bam.loadSync(bam.homePath + "bam.popModule.js");
            bam.popModule.init({
                css : "/shared/components/gameday/v6/css/gda-login-popModule.css"    
            });
        }

		_self.trigger("shareClick", ["Embed"]);
		var html = getEmbedHTML(),
            textarea;
        
        //console.log("embed html:", html);

        bam.popModule.show({
            htmlContent : html,
            postShow : function() {
                textarea = $("#module_content").find("textarea");
                textarea.click(function() {
                    this.focus();
                    this.select();
                });
            }
        });

        bam.popModule.postExit = function() {
            if (textarea) { 
                textarea.unbind();
            }
        };

        return false;

    });
	
	return $.bindable(_self);

}; // end of ClipDataManager





/**
 * PlaylistWidget provides carousel-style video (auto or not) playlist functionality
 */
bam.media.PlaylistWidget = function(){
																	 
	var _cfg = {
			containerId : null,
			clipsPerSet : 4,
			autoAdvance : true,
			debugMode   : false
		},
		_$ul,  // playlist dom ref,
		_playlist    = [],
		_curIndex    = 0,
		_isAnimating = false,
		_viewportWidth;
				
	
	/**
	 * Prev/next playlist handler. Shows previous or next set of playlist items
	 * and dis/enables the prev/next buttons accordingly.
	 */
	function _showSet(direction){
		if (!_isAnimating){
			var amount      = _viewportWidth * (direction*-1);
					origPosLeft = (_$ul.css("left")==="auto") ? "0px" : _$ul.css("left");
					posLeft     = +(origPosLeft.match(/^\-?\d+/)[0]),
					posLeftNew  = posLeft+amount;
			_$ul.animate({"left":posLeftNew+"px"}, 500, "easeOutExpo", function(){
				_isAnimating = false;
				if ($(this).css("left")==="0px"){ 
					$("#btnPlaylistPrev").addClass("disabled");
				}
				else {
					$("#btnPlaylistPrev").removeClass("disabled");
				}
				// if at end, disable right button
				if ((Math.abs(posLeftNew) + _viewportWidth) < _$ul.width()){
					$("#btnPlaylistNext").removeClass("disabled");
				}
				else {
					$("#btnPlaylistNext").addClass("disabled");
				}
				var firstClipShownIndex = (Math.abs(posLeftNew)/_viewportWidth) * _cfg.clipsPerSet;
				_self.trigger("showSetComplete", [firstClipShownIndex, direction]);
			});
			_isAnimating = true;
		}
	}
	

	var _self = {
		
		userInitiatedPlay : false, // for tracking user click vs. auto advance
		
		
		/**
		 * Initializes playlist and assigns handlers
		 * @param Object with custom config settings.
		 */
		init: function(/* config */){
			if (arguments[0]){
				$.extend(_cfg, arguments[0]);
			}
			_$ul = $("#"+_cfg.containerId);
			_$ul.show(); // show here to be able to get width of LIs
			
			var _$li               = _$ul.find("li:first"),
					rightMargin        = (_$li.length) ? +(_$li.css("margin-right").match(/^\d+/)[0]) : 0, // @TODO _$li may not exist - catch error
					playlistItemsWidth = _$ul.find("li").length * (_$li.width() + rightMargin);
					
			_viewportWidth = (_cfg.clipsPerSet * (_$li.width() + rightMargin));
			
			_$ul
				.parent().width(_viewportWidth + "px")
				.end()				
				.width(playlistItemsWidth + "px")
				.find("a").each(function(){ 
					_playlist.push($(this).attr("rel")); // @TODO optimize
				});
				
			_self.setInitialNextButtonState();
			
			// assign prev/next button click handler
			$(".btnPlaylist").live("click", function(){
				if (!$(this).hasClass("disabled")){
					_showSet( +$(this).attr("data-page-by") );
				}
			});
			// assign clip click handler
			$("#"+_cfg.containerId+" a").live("click", function(e){
				e.preventDefault();
//				if (e.button !== 0){
//					return true; // ignore right-click (firefox bug)
//				}
				$(this).blur();	
				_curIndex = $(this).parent("li").attr("index");  // @TODO use data-index instead of index
				_self.play();
				_self.userInitiatedPlay = true;
			});
			_self.trigger("afterInit");
		},
		
		
		/**
		 * Public contentId accessor
		 */
		getCurrentContentId: function(){
			return _playlist[_curIndex];
		},
		
		
		/**
		 * Set initial prev/next button state
		 */
		setInitialNextButtonState: function(){
			_self.log("setInitialNextButtonState");
			if (_$ul.find("li").length > _cfg.clipsPerSet){
				$("#btnPlaylistNext").removeClass("disabled");
			}
		},
		 
		
		/**
		 * Plays current clip by callling startPlaylist on the flvPlayer.
		 * @param content_id String
		 */
		play: function(/* contentId */){
			var contentId = arguments[0] || _playlist[_curIndex] || false;
			
			//console.log(_playlist)
			
			
			if (!contentId){
				_self.endPlaylist();
				return;
			}
			// highlight item
			_$ul.find("li.current").removeClass("current");			
			_$ul.find("li:eq("+_curIndex+")").addClass("current");
			_self.trigger("play", [contentId, +_curIndex, _self.userInitiatedPlay]);
		},
		
		
		/**
		 * Handler called when clip playback is complete.
		 */
		playEnd: function(){
			_self.log("playEnd: tracking and incrementing currentIndex");
			_self.userInitiatedPlay = false; // reset switch				
			_curIndex++;
			_self.trigger("playEnd");
			if (_cfg.autoAdvance){
				_self.log("playEnd: calling play()");
				if ( ((_curIndex+1) % _cfg.clipsPerSet) === 1){
					_showSet(1);
				}
				_self.play();
			}
			else {
				_self.log("playEnd: stoping playback.");
				_self.endPlaylist();
			}
		},
		
		
		endPlaylist: function(){
			_self.trigger("endPlaylist");
		}
		
	};

	return $.bindable(_self);
	
}; // end of PlaylistManager





/**
 * Video Playback Page
 */
bam.media.vpp = (function(){
													
	var _cfg = {
			// topic config
			topicId         : "",
			topicName       : "",
			autostart       : true,
			autoplaylist    : false,
			disableComments : false,
			maxClips        : 60,
			clipsPerPage    : 12,
			property		: "mlb",
			debugMode : false,

            skinParam : null,

			autostartTimeoutId : null,
			autostartDelay     : 10000,
			isPlaylistComplete : false,
			
			clipsPerPlaylistSet : 4,
			
            baseUrl          : "http://" + document.domain,
            selfUrl          : "/video/play.jsp",
			contentUrlBase   : "/video/play.jsp?content_id=",		
			prodPageUrl      : "http://mlb.mlb.com/video/play.jsp", // used for Facebook link
			searchServiceUrl : "", // unique per property (loaded on init)
			searchParams     : "type=json&src=vpp&start=0&sort=desc&sort_type=date",
			
			playlistContainer : "playlistItems ul",

            currentTime : new Date(), // used for calculating embeddable threshhold. should be overwritten by server generated date in page config.
			
			// configuration for instance of bam.FlvPlayer
			flvPlayer : {
				skin           : "/flash/video/y2010/mlb_vpp2010.swf",
				videoAlign     : "center",
				videoScaleMode : "noScale",
				autoHideSkin   : true,
				elemId         : "flashPlayer",
				containerId    : "flvContainer",
				endPosterPath  : "/images/000000.gif",
				companionSize  : "300x250",
				width          : "640",
				height         : "360",
				volume         : 65,
				showCompanionAd : function() {
					var companionAds, ad, tagName, adContainerID = "videoCompanionAd";
					if (arguments.length > 0) {						
                       	companionAds = Array.prototype.slice.call(arguments[0]);
						if (companionAds.length > 0) {
							$.each(companionAds, function(i, companionAd) {
								tagName = (companionAd.type === "iframe") ? "iframe" : "div";
								ad = $("<" + tagName + " />", {
 									height : parseInt(companionAd.height, 10),
									width : parseInt(companionAd.width, 10)    
								});
								if (companionAd.type === "iframe") {
									ad.attr({
										"src" : companionAd.data,
										"marginwidth" : "0",
										"marginheight" : "0",
										"frameborder" : "0",
										"scrolling" : "no"
									});									
								} else {
									ad.html(companionAd.data);
								}                                    
								if (companionAds.length > 1) {
									ad.appendTo("#" + adContainerID);
								} else {
									$("#" + adContainerID).empty().append(ad);
								}                                    
							});
                        }
                    }
				}					
			},

            // config for clip data manager
			clipManager : {}

		},
		
		_playlist,  // instantiated on init
		_clipManager, // instantiated on init
				
		_commentsAreDisplayed = false;
	
	
	/**
	 * private method for closing comments/entry-form
	 */
	function _closeComments(){
		$("#comments").hide();
		$("#videoBrowseWrap").show();
		_commentsAreDisplayed = false;					
	}
	
	
	/**
	 * Loads thumbs for currently displayed images. 
	 * (Used to avoid loading them before they're needed.)
	 * NOTE that start and end don't refer to the playlist items, but the playlist 
	 * items which haven't been loaded yet (ie. are still lazy). With this in mind, 
	 * the start value is almost always 0
	 */
	function _loadPlaylistSetThumbs(/* start, end */){
		var start    = arguments[0] || 0,
				end      = arguments[1] || start+_cfg.clipsPerPlaylistSet,
				$lazyLIs = $("#"+_cfg.playlistContainer).find("li.lazy");
//		_self.log("_loadPlaylistSetThumbs("+start+","+end+")");
		if ($lazyLIs.length){
			$lazyLIs
				.slice(start, end)
				.removeClass("lazy")
				.find("img").each(function(){
					$(this).attr("src", $(this).attr("data-src"));
				});
		}
		return $lazyLIs.length;
	}
	
			
	var _self = {
		
		/**
		 * Configures Video Playback Page object. Use before calling init()
		 */
		configure: function(cfg){
			$.extend(true, _cfg, cfg);
			$.loggable(_self, "VPP", _cfg.debugMode);			
		},
		
		
		/**
		 * Initializes the Video Page app and starts playback
		 */
		init: function(){
			
			var isIPad = (bam.env && bam.env.client && bam.env.client.isIPad);
			$("#flvContainer, #featureInfo").removeClass("collapsed");
			// load first set of clip thumbs
			_loadPlaylistSetThumbs();
			
			// hack for display of player name
			if (_cfg.playerName){
				$("#playlistWrap h6 strong").text(_cfg.playerName);
			}
			
			
			/**
			 * create and initialize playlist widget
			 * 
			 * @see bam.media.PlaylistWidget()
			 * @see $.loggable()
			 */
			_playlist = new bam.media.PlaylistWidget();
			$.loggable(_playlist, "VPP.playlist", _cfg.debugMode); // make it loggable
			_playlist
				.bind("afterInit", function(){
					/**
					 * initialize browse component
					 * @see bam.media.browseModule()
					 * @see $.loggable()
					 */					 
					$.loggable(bam.media.browseModule, "VPP.browseModule", _cfg.debugMode);
					bam.media.browseModule.init({
						baseUrl      : _cfg.selfUrl,
						club         : _cfg.club,
                        skinParam    : _cfg.skinParam || null,
						topicId      : _cfg.topicId,
						topicName    : _cfg.topicName,
						maxClips     : _cfg.maxClips,
						clipsPerPage : _cfg.clipsPerPage,
						debugMode    : _cfg.debugMode
					});
				});
			_playlist.init({
				containerId : _cfg.playlistContainer, 
				clipsPerSet : _cfg.clipsPerPlaylistSet
			});
			
			/**
			 * create clip data manager
			 * 
			 * @see bam.media.ClipDataManager()
			 * @see $.loggable()
			 */		
			 
			 
			 			 	
			_clipManager = new bam.media.ClipDataManager($.extend(true, {}, _cfg.clipManager, {
				topicId   : _cfg.topicId,
				club      : _cfg.club,
                skinParam : _cfg.skinParam || null,
                currentTime : _cfg.currentTime,
                baseUrl : _cfg.baseUrl,
				property : _cfg.property,
				prodPageUrl : _cfg.prodPageUrl,
				shareVideoUrl : _cfg.shareVideoUrl,
				enableTagLinks : _cfg.enableTagLinks
			}));

			$.loggable(_clipManager, "VPP.clipManager", _cfg.debugMode);

			// for iPad, force DC ad
			//if (isIPad){
			if (isIPad && false){
				$.ajax({
				  url      : "/shared/scripts/bam/widget/dcRefreshableAd/dcRefreshableAd-1.0.0.min.js",
				  dataType : "script",
					async    : false
				});
				$("#videoCompanionAd")
					.empty()
					.dcRefreshableAd({width:300, height:250});
				isIPad.adCount = 0; // used to avoid refreshing the ad before first play
			}
			// create instance of flvPlayer (pass it player config, plus custom event methods)			
			_self.flashPlayer = new bam.FlvPlayer($.extend(true, _cfg.flvPlayer, {
				onPlayerLoaded   : _playlist.play,
				contentStarted : function(){ // track when clip starts (since there may be a preroll)
                    _self.log("content started");
					var clip = _clipManager.getClipData(_playlist.getCurrentContentId());
					bam.tracking.track({
						async_media: {
							mediaID        : clip.content_id+"|"+clip.playbackScenario,
							playerType     : "Flash",
							playerContext  : "Video Playback Page",
							playerFlavor   : _cfg.topicName, // @TODO update topicName
							contextVersion : "3.0",
							streamType     : "Progressive Download",
							bitRate        : clip.bitRate,
							actionGen      : _playlist.userInitiatedPlay
						}
					});
				}
			}));
			_self.flashPlayer.bind("playlistComplete", _playlist.playEnd);

			_playlist
				.bind("play", function(e, contentId, curIndex, userInitiated){ 				
					var clip = _clipManager.getClipData(contentId);
					if (clip){
						_clipManager.showClipInfo(clip);
						_self.flashPlayer.startPlaylist([{
							type       : "video",
							path       : clip.curVideoFlashUrl,
							content_id : contentId,
							duration   : bam.media.getDurationInSeconds(clip.duration)
						}]);
						// for iPad, refresh DC ad
						if (isIPad){
							if (isIPad.adCount!==0){
								$("#videoCompanionAd").dcRefreshableAd("refresh");
							}
							else {
								isIPad.adCount++;
							}
						}					
						// load playlist thumbs in background @TODO consider pulling this
						var interval = setInterval(function(){
							if (!_loadPlaylistSetThumbs(0, 1)){
								clearInterval(interval);
							}
						}, 2000);
					}
					else {
						// @TODO show clip unavailable message
					}
					_self.trigger("play", [contentId]); // expose play trigger					
					_self.trigger("playCurrentItem", [contentId]); // for backwards compatibility
					if (userInitiated){
						bam.tracking.track({
							async:{
								isDynamic    : false, 
								compName     : "Video Playback Playlist", 
								compActivity : "Video Playback Playlist: Thumbnail " + (curIndex+1) + " Link Click", 
								actionGen    : userInitiated
							}
						});
					}
				})
				.bind("playEnd", function(){
					bam.tracking.track({videoComplete:{playerContext:"Video Playback Page"}});
				})
				.bind("endPlaylist", function(){ 
					_self.flashPlayer.execute("exitFullScreen");
				})
				.bind("showSetComplete", function(e, firstClipShownIndex, direction){ 
					_loadPlaylistSetThumbs(0); // always start from zero since function selects currently hidden thumbs
					bam.tracking.track({
						async:{
							isDynamic    : true, 
							compName     : "Video Playback List", 
							compActivity : "Video Playback Featured Playlist " + (direction > 0 ? "Right" : "Left") + " Arrow", 
							actionGen    : true
						}
					});
				});
			
			_clipManager
				.bind("shareClick", function(e, type) {
                    var contentId = _playlist.getCurrentContentId(),

                        trackingParams = {
							isDynamic    : false, 
							compName     : "Video Playback Distribution/Comments", 
							compActivity : "Video Playback " + type + " Link Click", 
							actionGen    : true
						};

                    // This hack puts the content id of the current video in s.prop58 
                    trackingParams.club = (type === "Embed" || type === "Copy" || type === "Email Video") ? contentId : null;

					bam.tracking.track({
						async : trackingParams
					});

				})
				.bind("tagClick", function(){
					bam.tracking.track({
						async:{
							isDynamic    : false, 
							compName     : "Video Search Option", 
							compActivity : "Video Playback Search Tag Click", 
							actionGen    : true
						}
					});
				})
				.bind("commentButtonClick", function(){
					if (_commentsAreDisplayed){
						_closeComments();
					}
					else {
						/**
						 * Displays the comments module and hides the video index.
						 * @TODO Add logic for blocking this during video ads (based on custom events)
						 */
						var contentId = _playlist.getCurrentContentId();
						bam.comments_init();
						bam.comments.overrideLoginLinkParams({content_id:contentId, topic_id:_cfg.topicId});
						bam.comments.getComments(contentId);
						$("#videoBrowseWrap").hide();
						$("#comments").show();
						_commentsAreDisplayed = true;
					}
					bam.tracking.track({
						async:{
							isDynamic    : false, 
							compName     : "Video Playback Distribution/Comments", 
							compActivity : "Video Playback Comment Link Click", 
							actionGen    : true
						}
					});
				});
			
			$("#closeComments").live("click", _closeComments);


			bam.media.browseModule
				.bind("topicSelected", function(e, topicName){
					bam.tracking.track({
						async: {
							isDynamic    : true,
							compName     : "Video Playback Page Topic Panel", 
							compActivity : "VPP: Topic Panel: " + topicName,
							actionGen    : true
						}
					});
				})
				.bind("pageNumberClick", function(e, page){
					bam.tracking.track({
						async: {
							isDynamic    : true,
							compName     : "Video Playback Page Browse Module", 
							compActivity : "Video Playback Browse Page " + page + " Click",
							actionGen    : true
						}
					});					 
				})
				.bind("pageDirectionClick", function(e, direction, page){
					bam.tracking.track({
						async: {
							isDynamic    : true,
							compName     : "Video Playback Page Browse Module", 
							compActivity : "Video Playback Browse Direction " + direction + " Click",
							actionGen    : true
						}
					});	 
				});


				// track browse clips before following link
				$("#vppIndexBrowse a").live("click", function(e){
					e.preventDefault();
					var index = +$(this).parent().attr("data-index");
					bam.tracking.track({
						async: {
							isDynamic    : false,
							compName     : "Video Playback Browse Module", 
							compActivity : "Video Playback Browse Module: Thumbnail " + (index+1) + " Click",
							actionGen    : true
						}
					});
					location.href = this.href;
				});

			_self.bind("searchResultsRendered", _playlist.setInitialNextButtonState);
			
			_self.trigger("initialized");
		},
		
		
		/**
		 * Hits the search service with passed/default parameters and loads unique content
		 * by appending video clip to the DOM, and adding it to contentIds.
		 * @param props Object Configuration object
		 * @TODO revisit the concept of initially collapsing (hiding) not exposed items
		 */
		loadSearchResults: function(props){		
			
			_self.log("loadSearchResults()");
			
			
			var existingClips = [],
					curClipId     = "", // temp var for iterating content ids
					callback = (typeof props.callback==="function") ? props.callback : function(){};
				if (!_cfg.searchServiceUrl){
					_self.log("loadSearchResults: exiting since searchServiceUrl is blank!!!");
					callback();
					return;
				}
				_self.log("storing existing contentIds from index or request...");
				$("#"+_cfg.playlistContainer + " a").each(function(i, curA){
					curClipId = $(curA).attr("rel");
					if (curClipId){
						existingClips.push(curClipId);
					}
				});
				if (existingClips.length >= _cfg.maxClips){
					_self.log(existingClips.length + " results already displayed, search not required. executing callback and exiting.");
					callback();
					return;  // don't show more items than maxItems
				} else {
					_self.log("need more results from search...");
				}
				
				function appendCID(){				
					if(_cfg.property === "mlb"){
						_cfg.searchParams += _cfg.club ? "&c_id="+_cfg.club : "";
					} else if (_cfg.property === "milb"){
						_cfg.searchParams += _cfg.club ? "&sid="+_cfg.club : "";
					}					
				}	
							
				
				// Append c_id to the search params for all VTPs where the property is MLB but the club is not MLB. 
				if(_cfg.property === "mlb" && _cfg.club !== "mlb") {
					// turning this off for now
					// appendCID();
				}	
				
							

				if (props.useContentKeywords){
					appendCID();
				} else {
					if (props.searchParams)                            { _cfg.searchParams += "&"+props.searchParams; }
					if (_cfg.searchParams.indexOf("sort=") === -1)     { _cfg.searchParams += "&sort=desc"; }
					if (_cfg.searchParams.indexOf("sort_type=") === -1){ _cfg.searchParams += "&sort_type=custom"; }
				}
				
				if(!~_cfg.searchParams.indexOf("src=vpp")) _cfg.searchParams += "&src=vpp";				
				
				_self.log("loadSearchResults: searchParams = "+ _cfg.searchParams);
				// load related content (param-based search only so not passing contentId or topicId)
				_self.log("about to call load from vpp.searchResults");				
				bam.media.relatedContent.load({
					contentId               : props.contentId,
					useContentKeywords      : props.useContentKeywords || false,
					ignoreContentIds        : existingClips,
					searchKeywordCategories : _cfg.searchKeywordCategories,
					searchQuery             : _cfg.searchParams,
					maxItemsOverride        : _cfg.maxClips,
					success : function(index, topicConfig){
						var html              = "",
								existingItemCount = existingClips.length,
								isLazy            = false,
								lazyClass         = "";
						$.each(index, function(i, curItem){
							if (existingItemCount>=_cfg.clipsPerPlaylistSet && !isLazy){
								isLazy    = true;
								lazyClass = ' class="lazy"';
							}
							html += '' + 
								'<li index="'+existingItemCount+'" '+lazyClass+'>' + 
									'<a href="'+ _cfg.selfUrl + '?content_id='+curItem.content_id+ (_cfg.skinParam ? _cfg.skinParam : "") +'" rel="'+curItem.content_id+'">' + 
										'<img src="'+( !isLazy ? curItem.thumb : '/images/trans.gif' )+'"' + ( !isLazy ? '' : ' data-src="'+curItem.thumb+'"' ) + ' />' + 
										'<div class="playBtn"></div>' + 
										'<p>'+curItem.headline+'</p>' + 
										'<div class="dateAdded">Added: '+curItem.date_added+'</div>' + 
										'<div class="duration">Duration: '+curItem.duration+'</div>' + 
									'</a>' + 
								'</li>';
							existingClips.push(curItem.contentId);
							existingItemCount++;
						});
						$("#"+_cfg.playlistContainer).append(html);	
						_self.trigger("searchResultsRendered");
					}
				});
			callback(); 
		},
		
		getConfig : function(){
			return _cfg;
		}
		
	};

	return $.bindable(_self);
															
})();
bam.vpp = bam.media.vpp;
