/**
 * The FacebookConnector is responsible for managing the tuner's communication
 * with the facebook api
 *
 * Somewhat helpful documentation for the facebook api and the oauth protocol
 *  http://wiki.developers.facebook.com
 */


function FacebookConnector(fbApiClient, apiKey, homepageUrl) {
    this._apiKey = apiKey;
    this._fbApiClient = fbApiClient;
    this._homepageUrl = homepageUrl;
    this._dataFromTuner = {};
    this._showConfirmationOnPublish = false;
}

FacebookConnector.IMAGESERVER_PREFIX = "http://www.pandora.com/art/";
FacebookConnector.CROSS_DOMAIN_CHANNEL = "/facebook/xd_receiver.htm"; // This url is relative to the connect url in the fb app settings

FacebookConnector.init = function(apiKey, homepageUrl) {
    FacebookConnector._instance = null;

    FB_RequireFeatures(["XFBML"], bind(this, function() {
        FB.init(apiKey, FacebookConnector.CROSS_DOMAIN_CHANNEL, {"forceBrowserPopupForLogin": true});

        FB.ensureInit(bind(this, function() {
            FacebookConnector._instance = new FacebookConnector(FB.Facebook.apiClient, apiKey, homepageUrl);
        }));
    }));
};

FacebookConnector.getInstance = function() {
    return FacebookConnector._instance;
};

FacebookConnector.prototype.share = function(/* string|null */ userToken, /* string|null */ userTokenSecret) {
    if (isEmpty(userToken)){
        // if we don't have a user token, we care about localconnection failures
        // if we don't have a user token, the listener will need to authenticate,
        // and after authentication, the browser needs to call back into the tuner
        // to save the credentials.
        Pandora.markTwitterFacebookConnectorAvailable("FacebookConnector_onLocalConnectionVerification");
    }else{
        // if we have credentials, 99% of the time the browser won't need to call back
        // into the tuner, and so we're not so worried if the localconnection is out
        // so there's no need to timeout this call
        this.onLocalConnectionVerification(userToken, userTokenSecret);
    }
};

FacebookConnector.prototype.onLocalConnectionVerification = function(/* string|null */ userToken, /* string|null */ userTokenSecret) {
    if (!isEmpty(userToken)) {
        this._showConfirmationOnPublish = false;  // the listener has already seen the confirmation dialog

        // we already have session tokens saved on the server - use those for the session
        this.setFacebookSession(userToken, userTokenSecret);

		this.publish();
    }else{
        this._showConfirmationOnPublish = true; // the listener has no yet seen the confirmation dialog

		this._fbApiClient.users_isAppUser(bind(this, function(isAppUser){
			// if we don't have credentials, but the listener currently logged into facebook has our app,
			// we should force them to logout as this would indicate that this is a shared machine and 
			// the current facebook session isn't owned by the current pandora listener
			if (isAppUser){
				this.logout();
			}else{
				this.auth();
			}
		}));
    }
};

FacebookConnector.prototype.logout = function() {
	// when this window is closed, 
	// -- facebook calls facebookConnectorCallback.html 
	// -- facebookConnectorCallback.html calls this.onAuthwindowCallback() 
	// -- this.onAuthwindowCallback() calls this.auth()
	this.openWindow("http://www.facebook.com/logout.php"
							+ "?app_key=" + this._apiKey 
							+ "&session_key=" + this._fbApiClient.get_session()["session_key"]
							+ "&next=" + this._homepageUrl + "radio/include/facebookConnectorCallback.html?logoutReturn=1");
};

FacebookConnector.prototype.auth = function() {
    Pandora.setFacebookShareUserTokens(null, null);

	// when this window is closed, 
	// -- facebook calls facebookConnectorCallback.html 
	// -- facebookConnectorCallback.html calls this.onAuthwindowCallback() 
	// -- this.onAuthwindowCallback() calls this.publish()
	this.openWindow('http://www.facebook.com/login.php'
							+ '?api_key=' + this._apiKey
							+ '&display=popup'
							+ '&extern=1'
							+ '&fbconnect=1'
							+ '&req_perms=publish_stream,offline_access'
							+ '&return_session=1'
							+ '&v=1.0'
							+ '&next=' + this._homepageUrl + "radio/include/facebookConnectorCallback.html?success=1"
							+ '&cancel_url=' + this._homepageUrl + "radio/include/facebookConnectorCallback.html?success=0"
							+ '&fb_connect=1');
}

FacebookConnector.prototype.publish = function ()
{
    if (this._showConfirmationOnPublish){
        Pandora.showFacebookTwitterShareConfirmation();
    }

    var postData = this.buildPostData(this._dataFromTuner);

    // http://wiki.developers.facebook.com/index.php/JS_API_M_FB.ApiClient.callMethod
    // NOTE: This usage of the new (beta) stream.publish is considered preferred API, except its formatting is less flexible than its
    // older feeds.publishUserAction cousin that accepts feed templates which allow some very simple markup.
    this._fbApiClient.callMethod("stream.publish", postData, bind(this, function(result, exception)
    {
        if (!result) {
            /*
             Code	Description
             1	    An unknown error occurred.
             100	    Invalid parameter.
             102	    Session key invalid or no longer valid (if it's a desktop application and the session is missing).
             200	    Permissions error. The application does not have permission to perform this action.
             210	    User not visible. The user doesn't have permission to act on that object.
             340	    Feed action request limit reached.
             */
            var errorCode = parseInt(exception.userData["error_code"]);

            if (errorCode == 102
					|| errorCode == 200
					|| errorCode == 210) {
				// some sort of permissions error.  re-auth and try everything again
				this.auth()
            } else {
				// some other error.  get out of dodge.
                var apiMethodName = exception.userData["request_args"][0]["value"];
                var errorMessage = exception.userData["error_msg"];

                Pandora.onFacebookPublishFailed(apiMethodName + "|" + errorCode + "|" + errorMessage);
            }

            return;
        }

        Pandora.onFacebookPublishSuccess();
    }));
};

FacebookConnector.prototype.openWindow = function(url) {
	var win = window.open(url, '_blank', 'top=442,width=480,height=460,resizable=yes', true);

	if (win == null){
		onPopupBlocker();
		Pandora.onFacebookPublishFailed("window blocked by popup blocker");
	}else{
		win.focus();
	}
}

FacebookConnector.prototype.onAuthwindowCallback = function(queryString) {
	// parse the results
	var results = {};
	queryString = queryString.substring(1); // knock of the ? prefix
	var splitString = queryString.split("&");;
	for (var i = 0; i < splitString.length; i++){
		var resultPair = splitString[i];
		var resultPairArray = resultPair.split("=");
		results[resultPairArray[0]] = resultPairArray[1];
	}

	if (parseInt(results.logoutReturn) == 1){
		this.auth();
		return;
	}

	if (parseInt(results.success) != 1){
		Pandora.setFacebookShareUserTokens(null, null);
		Pandora.onFacebookPublishCanceled();
		return;
	}

	eval('var session = ' + unescape(results.session));
	eval('var permissionsArray = ' + unescape(results.permissions));
	
	var permissions = {};
	for (var i = 0; i < permissionsArray.length; i++){
		permissions[permissionsArray[i]] = true;
	}


	if (!permissions.publish_stream){
		Pandora.setFacebookShareUserTokens(null, null);
		Pandora.onFacebookPublishCanceled();
		return;
	}

	if (permissions.offline_access){
		Pandora.setFacebookShareUserTokens(session["session_key"], session["secret"]);
	}else{
		Pandora.setFacebookShareUserTokens(null, null);
	}

	this._showConfirmationOnPublish = true;
	this.setFacebookSession(session["session_key"], session["secret"]);
	this.publish();
};

FacebookConnector.prototype.setFacebookSession = function(/* string|null */ userToken, /* string|null */ userTokenSecret) {
    this._fbApiClient.set_session({
        session_key: userToken,
        secret: userTokenSecret,
        sig: this.buildSig([
            ["session_key", userToken],
            ["secret", userTokenSecret]
        ], userTokenSecret)
    });
};

//http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application
FacebookConnector.prototype.buildSig = function (inArgs, sessionSecret) {
    var args = "";

    for (var i = 0; i < inArgs.length; i++) {
        args += inArgs[i][0] += "=" + inArgs[i][1];
    }
    return md5(args + sessionSecret);
};


FacebookConnector.prototype.startNewPost = function(/* string|null */ userToken, /* string|null */ userTokenSecret) {
    this._dataFromTuner = {};
};

FacebookConnector.prototype.onPostData = function(dataName, dataValue) {
    this._dataFromTuner[dataName] = dataValue;
};

FacebookConnector.prototype.buildPostData = function(dft) {
    if (dft["songId"] != null) {
        // if we have a songId, we are sharing a song.
        var imagePreviewUrl = (dft["artPreviewUrl"].indexOf("http://") != 0)
                ? FacebookConnector.IMAGESERVER_PREFIX + dft["artPreviewUrl"]
                : dft["artPreviewUrl"];

        var imageSrcUrl = (dft["artUrl"].indexOf("generic_cover_art") == -1)
                ? FacebookConnector.IMAGESERVER_PREFIX + dft["artUrl"]
                : dft["artUrl"];

        var audioSampleUrl = this._homepageUrl + "favorites/getSample.jsp"
                + "?token=" + dft["songToken"]
                + "&allowExplicit=" + dft["explicitContentFilterEnabled"];

        var swfSrc = this._homepageUrl + "static/facebook.swf"
                + "?srcUrl=" + encodeURIComponent(imageSrcUrl)
                + "&soundClip=" + audioSampleUrl
                + "&hrefUrl=" + encodeURIComponent(dft["landingpageUrl"]);

        return {
            message: dft["userMessage"],

            action_links: [
                {
                    text: "Listen at Pandora",
                    href: this._homepageUrl + "?ext_lsfmi=mf" + dft["songId"] + "%7C" + dft["stationId"]
                }
            ],

            attachment: {
                href: dft["landingpageUrl"],
                name: dft["songName"],
                caption: "by " + dft["artistName"],
                description: "on " + dft["albumName"],

                media:[
                    {
                        type: 'flash',
                        imgsrc: imagePreviewUrl,
                        swfsrc: swfSrc,
                        height: 100,
                        width: 100,
                        expanded_height: 100,
                        expanded_width: 100
                    }
                ]
            }
        };
    } else {
        // we are sharing a station
        var media = [];
        while (true) {
            var albumArtUrl = dft["albumArtUrl" + media.length];

            if (isEmpty(albumArtUrl)) {
                break;
            }

            media.push({
                type: "image",
                src: FacebookConnector.IMAGESERVER_PREFIX + albumArtUrl,
                href: dft["landingpageUrl"]
            });
        }

        return {
            message: dft["userMessage"],

            action_links: [
                {
                    text: "Listen at Pandora",
                    href: this._homepageUrl + "?ext_lsfi=sf" + dft["stationId"]
                }
            ],

            attachment: {
                caption: dft["stationName"] + " on Pandora will play music by " + dft["relatedArtistNames"],

                media: media
            }
        };
    }
};



/////////////////////////////
/// Tuner Callback hooks ////
/////////////////////////////
function FacebookConnector_startNewPost() {
    FacebookConnector.getInstance().startNewPost();
}

function onPostData(/* [dataName, dataValue] */ args) {
    FacebookConnector.getInstance().onPostData(args[0], args[1]);
}

function FacebookConnector_share(/* [userToken, userTokenSecret] */ args) {
    FacebookConnector.getInstance().share(args[0], args[1]);
}

function FacebookConnector_onLocalConnectionVerification() {
    FacebookConnector.getInstance().onLocalConnectionVerification();
}

