/**
 * Media Service 2.3 client
 * Capable of making SOAP and REST requests to MediaService 2.3
 *
 * @requires /shared/external/jquery.bindable.js 
 */

// TODO: check for $.bindable before applying the behavior

(function( $ ){
    bam.loadSync( bam.homePath + 'bam.xml.js' );
	bam.loadSync( bam.homePath + 'bam.cookies.js' );

    var window        = this,
        MediaService  = {},
	    _proxy        = "/pubajaxws/services/MediaService2_3",
	    _devURL       = "http://dev-a.services.bamnetworks.com:8888/ws/services/MediaService2_3",
        RESTENDPOINT  = '/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3',
	    _consoleLevel = 0,
		_waitingRoom = false,
        defaultParams;

    // make bindable if plugin is present. wrapped in a conditional for backwards-compatibility
    if( !!$.bindable ) {
        $.bindable( MediaService );
    }

	defaultParams = {
		isVerifiedUserRequest : true,
		event_id              : null,
		content_id            : null,
		playback_scenario     : null,
		subject               : 'LIVE_EVENT_COVERAGE',
		session_key           : null
	};

	/**
	 * Alias for debugging logger
     *
     * @private
	 */
	function _console(method,arg){
		if ( _consoleLevel>0 ){
			if ( typeof window.console!=='undefined' ){ console[method](arg); }
			else if (typeof arg==='string')	{ throw new Error(arg); } 
		}
	}

	/**
	 * Parses status node of Media Service 2.0 SOAP response 
     *
     * @private
	 */
	function _getStatus( responseBodyElement ) {
		var status = {
			code : responseBodyElement.status_code[0].Text
		};

		switch (status.code){
			case '-1000': 
				status.msg = 'Requested media not found';
				break;
			case '-2000':
				status.msg = 'Invalid App Account Information';
				break;
			case '-3000':
				status.msg = 'Invalid user credentials';
				break;
			case '-3500':
				status.msg = 'Sign-on restriction status';
				break;
			case '-4000':
				status.msg = 'System Error';
				break;
			default:
				status.msg = 'Unknown Error';
				break;
		}
	
		return status;
	}

	/**
	 * Creates request body for Media Service SOAP request
     * @private
	 */
	function _createRequestBody(requestBodyElementName){
		// Create the request soap object
		var bodyObj    = new bam.soap.SOAPObject( requestBodyElementName );
			bodyObj.ns = "http://services.bamnetworks.com/media/types/2.0";

		return bodyObj;
	}

	/**
	 * Creates identification node
     * @private
	 */
	function _createFprtIpidNode(){
		var ipid      = bam.cookies.get('ipid'),
		    fprt      = unescape( bam.cookies.get('fprt') ),
		    fprt_ipid = null;

		if ( !ipid ) { _console('log','MediaService2_0: missing identity point id'); }
		if ( !fprt ) { _console('log','MediaService2_0: missing fingerprint'); }

		if (!!ipid && !!fprt){
			fprt_ipid = new bam.soap.SOAPObject("fingerprint-identity-point");
			fprt_ipid.appendChild(new bam.soap.SOAPObject("identity-point-id").val(ipid.toString()));
			fprt_ipid.appendChild(new bam.soap.SOAPObject("fingerprint").val(fprt.toString()));
		}

		return fprt_ipid;
	}

	/**
	 * Builds SOAP structure for Media Service 2.0 request
     * @private
	 */
	function _appendMediaRequestNodes( soapBody, mediaParams ){ //event_id, ipid, fprt, content_id, playback_scenario
		var mediaParamValue, isVerifiedUserRequest, sessionKey;
		if (typeof mediaParams==='object'){

			isVerifiedUserRequest = mediaParams.isVerifiedUserRequest;
			delete mediaParams.isVerifiedUserRequest;

			sessionKey = mediaParams.session_key;
			delete mediaParams.session_key;

			for (var mediaParam in mediaParams){
				if (!!mediaParams[mediaParam]){
					mediaParamValue = mediaParams[mediaParam].toString();
					soapBody.appendChild(new bam.soap.SOAPObject(mediaParam.replace('_','-')).val(mediaParamValue));
				}
			}

			if (!!isVerifiedUserRequest)	{			
				var fprtIpid = _createFprtIpidNode();
				if (!!fprtIpid) { soapBody.appendChild(fprtIpid); }
			}

			if (!!sessionKey){
				soapBody.appendChild(new bam.soap.SOAPObject('session-key').val(sessionKey.toString()));
			}
		}
		else{
			throw new Error('MediaService2_0: invalid mediaParams object');
		}
	}

	/**
	 * Checks if response contains service error
     * @private
	 */
	function _isErrorThrown(responseBodyElement){
		return responseBodyElement.status_code && responseBodyElement.status_code[0].Text != 1;
	}

	/**
	 * Converts media service 2.0 soap response to JSON
     * @private
	 */
	function _soapToMediaResponse(soapObject){
		var mediaResponse = null;

		if (typeof soapObject==='object'){

			if (!!soapObject._children || !!soapObject._attributes){
				if (!!soapObject._children){
					var c=0, childName, child;
					mediaResponse = {};

					do{
						childName                = soapObject._children[c];
						child                    = soapObject[childName];
						mediaResponse[childName] = child.length>1 ? _soapToMediaResponse(child) : _soapToMediaResponse(child[0]);
						c++;
					}
					while (c<soapObject._children.length);
				}
			}
			else if (soapObject.length>1){
				var i=0;
				mediaResponse = [];
				do{
					if (!!soapObject[i]._children || !!soapObject[i]._attributes){ 
						if (!!soapObject[i]._attributes && !!soapObject[i].name){
							mediaResponse.push({name:soapObject[i].name, value:soapObject[i].Text});
						}
						else {
							mediaResponse.push(_soapToMediaResponse(soapObject[i])); 
						}
					}
					else if (!!soapObject[i].Text){ 
						mediaResponse.push(soapObject[i].Text); 
					}
					i++;
				} while(i<soapObject.length);
			}
			else if (!!soapObject.Text){
				mediaResponse = soapObject.Text;
			}	
		}

		return mediaResponse;
	}

    /**
     * Returns normalized media request data object for REST endpoint
     *
     * @private
     */
	function createMediaRequestObject( mediaParams ) {
		var mediaParamValue, isVerifiedUserRequest, paramObj = null, arrParamName;
		if (typeof mediaParams==='object'){
			paramObj = {};

			isVerifiedUserRequest = mediaParams.isVerifiedUserRequest;
            if( !!mediaParams.success ) { delete mediaParams.success; }
            if( !!mediaParams.error ) { delete mediaParams.error; }
            // cache clearing param. needed for akamai.
            //if( isVerifiedUserRequest ) {
                // mediaParams.c = new Date().valueOf();
            //}
			delete mediaParams.isVerifiedUserRequest;
			delete mediaParams.requestType;

			if ( !!isVerifiedUserRequest && !mediaParams.partner_token )	{			
				var ipid      = bam.cookies.get('ipid');
				var fprt      = unescape(bam.cookies.get('fprt'));
				var fprt_ipid = null;

				if (!!!ipid) { _console('log','MediaService2_0: missing identity point id'); }
				if (!!!fprt) { _console('log','MediaService2_0: missing fingerprint'); }

				if (!!ipid && !!fprt){
					paramObj.identityPointId = ipid.toString();
					paramObj.fingerprint     = fprt.toString();
				}
			}

			for (var mediaParam in mediaParams){
				if (!!mediaParams[mediaParam]){
					mediaParamValue = mediaParams[mediaParam].toString();
					if (mediaParam.indexOf('_')!==-1){
						arrParamName = mediaParam.split('_');
						for (var w=1; w<arrParamName.length; w++){
							arrParamName[w] = arrParamName[w].charAt(0).toUpperCase() + arrParamName[w].substring(1);
						}
						mediaParam = arrParamName.join('');
					}

					paramObj[mediaParam] = mediaParamValue;
				}
			}
		}
		else{
			throw new Error('MediaService2_0: invalid mediaParams object');
		}

        paramObj.platform = "WEB_MEDIAPLAYER";

		return paramObj;
	}
	
    /**
     * Sends SOAP request to MediaService
     */
	function sendSOAPRequest( mediaParams ){
			var params  = $.extend( defaultParams, mediaParams ),
                success = (!!mediaParams.success && typeof mediaParams.success === 'function') ? mediaParams.success : null,
			    error   = (!!mediaParams.error && typeof mediaParams.error === 'function') ? mediaParams.error : null;

			_console('group','MEDIA SERVICE 2.0: Request Media');

			bam.soap.SOAPClient.Proxy      = _proxy;
			bam.soap.SOAPClient.SOAPServer = _devURL;

			var soapBody       = _createRequestBody("user-verified-media-request");
			var requestElement = _appendMediaRequestNodes(soapBody, params);

			var soapRequest = new bam.soap.SOAPRequest("find", soapBody);
			bam.soap.SOAPClient.SendRequest(soapRequest, function(data)	{
				if (data && data.Body && data.Body[0].user_verified_media_response) {
					var responseBodyElement = data.Body[0].user_verified_media_response[0];

					if ( !_isErrorThrown(responseBodyElement) ) {
						if ( !!success ) {
							var mediaResponse = _soapToMediaResponse(responseBodyElement);
							_console('dir',mediaResponse);
							success(mediaResponse);
						} else {
							_console('error','MediaService2_0.requestMedia: success handler is not a function');
						}
					} else {
						if (typeof error === 'function'){
							var oError       = _getStatus(responseBodyElement);
							oError.operation = "MediaService.requestMedia";
							error(oError);
						} else{
							_console('error','MediaService2_0.requestMedia: errorCallback is not a function');
						}
					}
				} else {
					if ( !!error ) {
						error({operation:'MediaService2_0.requestMedia', code:-100000, message:'bad data'});
					} else {
						_console('error','MediaService2_0.requestMedia: errorCallback is not a function');
					}
				}
			});

			_console('groupEnd','END MEDIA SERVICE 2.0: Request Media'); 
	}


    /**
     * Initiates media request to MediaService's REST endpoint. This method also handles the Netscaler waiting room.
     */
    // TODO: rewrite callbacks to be event-driven
	function sendRESTRequest( mediaParams ){
			var success          = ( !!mediaParams.success && typeof mediaParams.success === 'function' ) ? mediaParams.success : null,
			    error            = ( !!mediaParams.error && typeof mediaParams.error === 'function') ? mediaParams.error : null,
                mp               = $.extend( {}, defaultParams, mediaParams ),
			    requestObject    = createMediaRequestObject( mp );


			$.ajax({
                url     : ( !!mediaParams.use_proxy ) ? '/media/msp.jsp' : RESTENDPOINT,
				data    : requestObject,
                cache   : false,
				success : function( response ){

					var data,
					    waitingRoomToken = bam.cookies.get('NSC_BPIP'),
                        mediaResponse,
                        oError;

					// if netscaler drops the waiting room cookie, handle it
					if (!!waitingRoomToken && !_waitingRoom) { 

						_waitingRoom = true;

						setTimeout(function() {
							MediaService.requestMedia( mediaParams ); 
							bam.cookies.remove({
                                name : 'NSC_BPIP',
                                path : '/'
                            });
						}, 1000);
					
					// process service response
                    } else {

						_waitingRoom = false;
                        
                        // clear waiting room cookie, if for some reason we still have one
                        bam.cookies.remove({
                            name : 'NSC_BPIP',
                            path : '/'
                        });

						data = bam.xml.xmlToJSON(response);

                        // we got an actual response back
						if (data.RootName==='user_verified_media_response') {

                            // response contains media
							if (!_isErrorThrown(data)){
								mediaResponse = _soapToMediaResponse(data);
								_console('dir',mediaResponse);

                                // wrapped in a conditional for backwards-compatibility
                                if( !!$.bindable ) {
                                    MediaService.trigger( 'requestMedia:success', [ mediaResponse ] );
                                }

                                // legacy callback support
								if (typeof success === 'function'){
									success(mediaResponse);
								} else {
									_console('error','MediaService2_0.requestMedia: success handler is not a function');
								}
                                
                            // response contains error 
							} else {
								oError           = _getStatus(data);
								oError.operation = "MediaService.requestMedia";
							}

                        // service reponsed with bad data
						} else {
                            oError = {
                                operation : 'MediaService2_0.requestMedia', 
                                code      : -100000,
                                message   : 'Bad Data in Response'
                            };
						}

                       // trigger error event
                       if( !!oError ) { 
                            // wrapped in a conditional for backwards-compatibility
                            if( !!$.bindable ) {
                                MediaService.trigger( 'requestMedia:error', [ oError ] );
                            }

					        if (typeof error === 'function'){
					        	error(oError);
					        } else {
					        	_console('error','MediaService2_0.requestMedia: errorCallback is not a function');
					        }
                       }

						_waitingRoom = false;
					}
				},

                error : function() {
                            
                    var oError = {
                        operation : 'MediaService2_0.requestMedia', 
                        code      : -100000,
                        message   : 'Bad Data in Response'
                    };
                    
                    // wrapped in a conditional for backwards-compatibility
                    if( !!$.bindable ) {
                        MediaService.trigger( 'requestMedia:error', [ oError ] );
                    }

                    if (typeof error === 'function'){
                        error(oError);
                    } else {
                        _console('error','MediaService2_0.requestMedia: errorCallback is not a function');
                    }
                }
            });


	}

	$.extend( MediaService,  {
        /**
         * Log level controls verbosity of proxy _console() function
         */
		setLogLevel: function(level){
			_consoleLevel = level;
		},

		/**
		 * Sends request for media to MediaService 2.0
         * 
         * @param mediaParams Configuration object for media request
         *                    Sample config:
         *                    {
         *                          event_id              : null, // calendar event id
         *                          content_id            : null, // content id
         *                          playback_scenario     : null, // playback scenario
         *                          subject               : 'LIVE_EVENT_COVERAGE', // subject
         *                          isVerifiedUserRequest : true, // requires authenticated user for response? must be true in order to get protected URLs
         *                          session_key           : null  // response includes session key, which is stored as a cookie (mssk). if that cookie is present, pass value back here 
         *                    }
		 */
		requestMedia: function( mediaParams ){
			if ( !!mediaParams.requestType && String.prototype.toLowerCase( mediaParams.requestType ) === 'soap' ) {

                bam.loadSync( bam.homePath + 'bam.soap.js' );
				sendSOAPRequest( mediaParams );

			} else {

				sendRESTRequest( mediaParams );

			}
		}
	});

	window.MediaService = MediaService;
})( jQuery );


