/**
 * Copyright (C) 2005 Brightcove, Inc.  All Rights Reserved.  No
 * use, copying or distribution of this work may be made except in
 * accordance with a valid license agreement from Brightcove, Inc.
 * This notice must be included on all copies, modifications and
 * derivatives of this work.
 *
 * Brightcove, Inc MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT
 * THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. BRIGHTCOVE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED
 * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS
 * SOFTWARE OR ITS DERIVATIVES.
 *
 * "Brightcove" is a trademark of Brightcove, Inc.
 **/

/*********************************************** CONFIGURATION ************************************************/

/**
 * This is the API Token assigned to each Brightcove customer that allows for the use of Brightcove's Media Read API.
 * Fill in your read API token (there are two versions, but you want the one that includes URL responses) here.
 */
var BCReadAPIToken = "Your token here...";

/**
 * This value indicates whether or not your account is set-up for UDS. HTML5 requires that the files be delivered
 * over HTTP.  This is accomplished by having an account that is configured for HTTP (PD) delivery or that is set-up
 * for UDS. 
 */
var isUDS = true;

/* This variable is a dictionary that contains information about the location of each
 * Brightcove video object within the DOM of the page. Specifically, it is an associative array
 * where, for each stored mapping, the keys is the playerID of a given video, and the value is
 * the next sibling of that video object in the DOM. Keeping track of this sibling will allow
 * us to re-insert the mobile compatible <video> tag into the correct place (before this sibling)
 * in the DOM of the original page.
 */
var pagePlacementInfo = new Object();

/**********************************************************************************************************************/
/*********************************************** DOM MODIFICATION CODE ************************************************/
/**********************************************************************************************************************/

/* This is the main entry function. It goes through the list of all video objects that need to be removed,
 * and one by one, initiates a request that causes that object to be removed and replaced by the
 * appropriate <video> tag (if the JS detects that the browser is on a smartphone).
 */
function runMobileCompatibilityScript(bcExperienceID, videoTagID){

	//detect if this is a smartphone or not
	var thisIsSmartPhone = DetectSmartphone();

	//if this is not a mobile browser, then don't bother doing anything. Leave the page as it is!
	if (!thisIsSmartPhone){
		return;	
	}
	
	makeMobileCompatible(bcExperienceID, videoTagID);
}

/* This method works on a specific object, represented by id "strObjID". The method retrieves the
 * element with the given ID from the DOM, and then extracts the player ID from the video
 * object. Then, it removes the video object from the page's DOM and stores its location in the page
 * in a global dictionary variable (this will be useful when we want to add the corresponding
 * video tag back in the page in the appropriate place).
 *
 * Finally, the method submits an API Read request to the Brightcove server through the initiateMobileVideoRetrieval()
 * method in order to retrieve the sepcific Video URL corresponding to the given object.
 */
function makeMobileCompatible(strObjID, videoTagID){
		
	//our video object (which we need to remove)
	var vidObj = document.getElementById(strObjID);
	
	//extract the playerID of this video object before deleting it
	var vidPlayerID = getParamValueForVidObject(vidObj, 'playerID');
	var programmedVideo = getParamValueForVidObject(vidObj, '@videoPlayer');

	//if the video player ID could not be extracted from the Source Code, for some reason,
	//then refer to the dictionary provided by the user
	if (vidPlayerID == null || typeof vidPlayerID == 'undefined'){
			vidPlayerID = BCVidObjects[strObjID];
	}

	//store the parent of the node we wish to remove
	var parentObj = vidObj.parentNode;
	
	//this is the object before which our vidObj element occurs in the parent element's DOM.
	//likewise, when we insert our <video> tag, we will insert it BEFORE this element,
	//in order to maintain the look of the page (this is the best that we can do...)
	var nextAdjacentNode = vidObj.nextSibling;
	
	//if there are no nodes after this node that was removed, then store 'null' to indicate that this was the last
	//child.
	if (nextAdjacentNode == null){
		pagePlacementInfo[""+vidPlayerID] = null;
	}
	//otherwise store the next sibling
	else {
		pagePlacementInfo[""+vidPlayerID] = nextAdjacentNode;	
	}
	
	//now, dynamically remove the video object from the DOM
	parentObj.removeChild(vidObj);

	//this procedure will make the appropriate API calls to get the first video corresponding to the player ID 
	//of the object we just removed.
	
	initiateMobileVideoRetrieval(vidPlayerID, programmedVideo, BCReadAPIToken, videoTagID);
}


/** 
 * This function takes an object representing a Brigthcove video embed and a particular 'parameter' that was
 * passed to the Brightcove video object and returns the parameter.
 */
function getParamValueForVidObject(vidObj, paramName) {
	//these are the children nodes of the given object in the DOM
	var childrenNodes = vidObj.childNodes;
	var tagName;
	
	//loop through all children of the video object, searching for <param> tags.
	//each time we find a <param> tag, we check whether its name is 'flashVars'.
	//if so, we store the param's value and break from the loop, otherwise we
	//continue
	for (var i = 0; i < childrenNodes.length; i++){
	    if (childrenNodes[i].nodeType != 1) {
		continue;
	    }

	    tagName = childrenNodes[i].tagName.toLowerCase();
	    if (tagName == "param"){
		if (childrenNodes[i].getAttribute("name") == paramName){
		    return childrenNodes[i].getAttribute("value");
		}
	    }
	}

	return null;
}

/**
 * Takes a string 'str' that consists of multiple arguments separated by ampersands (&),
 * and breaks it down so that it can extract and return the paramName from the string.
 */
function parseParamFromString(str, paramName) {
	var params = str.split("&"); //array of strings
	for (var i = 0; i < params.length; i++){
		if (params[i].indexOf(paramName) != -1){
			return params[i].substr(params[i].indexOf("=")+1);
		}
	}
	
	// if we could not find the param then return null
	return null;
}



/**********************************************************************************************************************/
/****************************************** MEDIA API CALLS & VIDEO TAG INSERTION *************************************/
/**********************************************************************************************************************/

/* This method calls the Brightcove Media API to get all playlists included within a particular
 * playerID.
 */
function initiateMobileVideoRetrieval(playerID, programmedVideoID, readAPIToken, videoTagID) {
		
    var APICall;
    var scriptNode;
    var scriptText;
    var callbackMethodName;

    if (programmedVideoID) {
	APICall = "http://api.brightcove.com/services/library?command=find_video_by_id&video_id="+programmedVideoID+"&token="
								+readAPIToken;


	//when we make the API call, we specify a response handler (known as a callback method) that will deal with the response from
	//the Brightcove server. However, we create a customized 'callback' method for each playerID, so that when we are 'inside' the
	//callback method (after receiving the server's reponse), we will know which playerID the response corresponds to. This variable
	//stores the name (which includes the playerID) of that callback method.
	callbackMethodName = "handleJSONResponseForID"+playerID;
	scriptNode = document.createElement("script");
	scriptNode.setAttribute("language", "javascript");
	scriptText = 
		"function "+callbackMethodName+"(JSONResponse){\n" + 
			"\thandleVideoResponse(JSONResponse, '"+playerID+"', '"+videoTagID+"');\n"+
		"}\n";
    }
    else {
	APICall = "http://api.brightcove.com/services/library?command=find_playlists_for_player_id&player_id="+playerID+"&token="
								+readAPIToken;
	callbackMethodName = "handleJSONResponseForID"+playerID;
	scriptNode = document.createElement("script");
	scriptNode.setAttribute("language", "javascript");
	scriptText = 
		"function "+callbackMethodName+"(JSONResponse){\n" + 
			"\thandlePlaylistResponse(JSONResponse, '"+playerID+"', '"+videoTagID+"');\n"+
		"}\n";
    }

    if (isUDS) {
	APICall += "&media_delivery=http";
    }

	//NOTE: we add to the end of the body, so that we do not disrupt any of the order of the children
	//at the top of the body's DOM tree
	scriptTextNode = document.createTextNode(scriptText);
	scriptNode.appendChild(scriptTextNode);
	document.body.appendChild(scriptNode);
	
	//make the API call, specifying the unique callback method for this request
	addScriptTag("getMobileRendition",  APICall, callbackMethodName);
}

/* Methods needed to make API Calls to the Brightcove server*/
function addScriptTag(id, url, callback) {
	
	var scriptTag = document.createElement("script");
	var noCacheIE = '&noCacheIE=' + (new Date()).getTime();
   
   // Add script object attributes
   scriptTag.setAttribute("type", "text/javascript");
   scriptTag.setAttribute("charset", "utf-8");
   scriptTag.setAttribute("src", url + "&callback=" + callback + noCacheIE);
   scriptTag.setAttribute("id", id);
	
	var head = document.getElementsByTagName("head").item(0);	
	head.appendChild(scriptTag);
}

/**
 * This is the general response-handler for the JSON response from the Brightcove server for playlist based players.
 * The arguments to the method include the response object, as well as the playerID of the 
 * object which this response pertains to.
 *
 */
function handlePlaylistResponse(JSONResponse, playerID) {
	
	//obtain first playlist in Brightcove Player given corresponding to this playerID
	firstPlaylist = JSONResponse.items[0];
	
	//obtain the first video from our first playlist
	firstVideo = firstPlaylist.videos[0];

	embedHTML5PlayerForVideo(firstVideo, playerID);
}

/**
 * This is the general response-handler for the JSON response from the Brightcove server for playlist based players.
 * The arguments to the method include the response object, as well as the playerID of the 
 * object which this response pertains to.
 *
 */
function handleVideoResponse(JSONResponse, playerID, videoTagID) {
    embedHTML5PlayerForVideo(JSONResponse, playerID, videoTagID);
}

/** 
 * For a given video object (from the BC APIs) we will embed an HTML 5 'video' tag.
 * Requires searching through the renditions associated with the video object
 * for a rendition that is a 'best' match and passing the URL to the video
 * tag.
 *
 * In this handler, we explore the JSON object in search of the first video in the
 * first playlist that is returned by the Brightcove server. Then, once we identify
 * this first video, we examine the various renditions of the video and search
 * for the rendition that is most appropriate for a mobile (H.264 encoding 
 * and 256 kbps). 
 */
function embedHTML5PlayerForVideo(video, playerID, videoTagID) {
	//obtain the array of various renditions that exist for this video.
	//NOTE: a rendition, from our perspective, has a certain encoding rate,
	//      and a certain encoding format. We wish to find the best rendition for
	//      a smartphone.

	renditions = video.renditions;

	//In the for-loop that follows, we traverse all renditions of this first video, searching
	//for the H.264 (mobile-compatible) rendition whose encoding rate is closest to 256kbps
	
	bestRenditionIndex = -1;
	bestEncodingRateSoFar = -1;
	
	if (renditions != null) {
	    for (var i = 0; i < renditions.length; i = i+1){
		//if this rendition is not H264, skip it and move on to the next
		if (renditions[i].videoCodec != "H264"){
		    continue;
		}
		
		//if best rendition index variable is uninitialized, then initialize it to
		//this rendition (which is H.264) - we need this because it's possible that
		//there are no H264 renditions at all, and starting our bestRenditionIndex at
		//an invalid value will help us figure out whether we came across any H264 renditions
		//as we were looping.
		if (bestRenditionIndex == -1){
		    bestRenditionIndex = i;
		    bestEncodingRateSoFar = renditions[i].encodingRate;
		}
		
		//otherwise check to see if this rendition has a better encoding rate than the best one before this
		else if (betterEncodingForMobile(renditions[i].encodingRate, bestEncodingRateSoFar) == renditions[i].encodingRate){
		    //if so, then record this rendition as the best one so far
		    bestRenditionIndex = i;
		    bestEncodingRateSoFar = renditions[i].encodingRate;
		}
	    }
	    
	}

	//after the for-loop has terminated, if best rendition index still == -1,
	//then that means we don't have ANY H264 renditions. so let the user know,
	//and don't add anything to the page
	if (bestRenditionIndex == -1){
	    bestRendition = video.videoFullLength;
	}
	else {
		bestRendition = renditions[bestRenditionIndex];
	}


	bestRenditionURL = bestRendition.url;

	vidName = video.name;
	vidHeight = bestRendition.frameHeight;
	vidWidth = bestRendition.frameWidth;
	vidStillURL = video.videoStillURL;
		
	//construct the <video> tag as a DOM element
	videoScriptTag = formVideoTagFromInfo(videoTagID, vidName, bestRenditionURL, vidWidth, vidHeight, vidStillURL);
	
	//retrieve the component before which this video tag needs to be inserted
	var nextSiblingOfVideo = pagePlacementInfo[playerID];
	var videoTagParent = nextSiblingOfVideo.parentNode; //the sibling and this video share the same parent node!
		
	//if 'nextSibling' value is null, then we want to add our video as the last child of the parent,
	//so we use the append() method; if 'nextSibling' is defined, then we use insertBefore() to add our video tag
	//into the appropriate location in our page.
/*
alert(videoScriptTag.width);
		alert(videoScriptTag.poster);
		alert(videoScriptTag.height);
		alert(videoScriptTag.controls);
		alert(videoScriptTag.src);
*/
//	var videoTag = '<video src="'+videoScriptTag.src+'" width="'+videoScriptTag.width+'" height="'+videoScriptTag.height+'" controls="'+videoScriptTag.controls+'" poster="'+videoScriptTag.poster+'"/>';



//var wrapper = document.createElement('div');	
	if (nextSiblingOfVideo == null){

	//			videoTagParent.appendChild(wrapper);

		videoTagParent.appendChild(videoScriptTag);
	}
	else{
		//		videoTagParent.insertBefore(wrapper, nextSiblingOfVideo);
					
		videoTagParent.insertBefore(videoScriptTag, nextSiblingOfVideo);

	}
//	wrapper.innerHTML = videoTag;
}

/* This function takes two encoding rates and returns the one that
 * is more apprporiate for mobile phones.
 */
function betterEncodingForMobile(encoding1, encoding2){
	IDEAL_ENCODING_RATE = 256000; //bits per second; equivalent to 256 kbps
	
	diff1 = Math.abs(encoding1 - IDEAL_ENCODING_RATE);
	diff2 = Math.abs(encoding2 - IDEAL_ENCODING_RATE);
	
	return ((diff1 <= diff2) ? encoding1 : encoding2);
}

/**
 * This method takes properties of a video, its dimensions, and its poster (still image),
 * inserts them into an HTML 5.0 <video> tag. This <video> object is then returned.
 */
function formVideoTagFromInfo(videoTagID, videoID, videoURL, vidWidth, vidHeight, vidImageURL){

	videoTag = document.createElement("video");
	if (videoTagID) {
	    videoTag.setAttribute("id", videoTagID);
	}
	else {
	    videoTag.setAttribute("id", videoID);
	}

	videoTag.setAttribute("poster", vidImageURL);
	videoTag.setAttribute("width",""+vidWidth);
	videoTag.setAttribute("height", ""+vidHeight);
	videoTag.setAttribute("controls", "true");
	videoTag.setAttribute("src", videoURL);
	
	return videoTag;
}

