/**
 * @fileOverview
 * Defines an easy-to-use DAAPI comments input tool; simply include the JS and replace 
 * gSiteLife.CommentsInput with kSiteLife.CommentsInput.
 * If Facebook Connect is configured, this automatically integrates it into the process.
 * 
 * MVC pattern:
 *   kSiteLife is the "black box" Controller object. It should not be modified except to fix bugs.
 *   kSiteLifeCustomizations is the View object, with all the GUI code and settings that you will need to change.
 * 
 * @author Glen Ford (gford--AT--pluck.com)
 * @namespace kSiteLife
 */


// \\\
// YOUR CUSTOMIZATIONS HERE \\
// \\\
/**
 * @class
 * Encapsulates all customizations to the behavior and GUI of kSiteLife, so that kSiteLife can
 * be a true black box.
 * This is "assigned" as a property on kSiteLife after both are defined (bottom of this file), so
 * if you change the name here, you must change it there as well (only 2 places).
 * 
 * This is the "View" in MVC (a.k.a. view tool).
 */
var kSiteLifeCustomizations = {
            getCommentMaxLength: function() { return 3000 },
            
            getDaapiProcessUrl: function() {
                        //returns the global for the URL (it's here so we don't dirty up the Controller with implementation-specific junk)
                        return serverUrl; //this could be fancier
            }
	,
	writeCommentGui: function() {
		/* quick and very dirty way of composing and writing the HTML scaffolding for comments input
		 * -- encapsulates all the dirtiness and lets kSiteLife be clean
		 * -- even dirtier... I'm doing old-skool DIVitis & CLASSitis... welcome to Y2K baby
		 * ---- (at least I'm using the LABEL tag)
		 * this should go without saying, but the IDs on elements must match what kSiteLife expects; this is the only bleed-over in the MVC pattern
		 */
		
		//write the HTML...
		//...start the container for the whole shebang
		document.write('<div id="kSiteLife">');
		
		/*...login/register links -- taken straight from the widget with different IDs, so the CSS is mostly 
		 * the same as a straight-up widget 
		 * -- KEEP IT IN SYNC!
		 * (array joining is way less expensive than string concatenation)
		 */
		document.write( [
			'<div id="kSiteLife_login" class="SiteLife_Login">Please log in to leave a comment. ',
			'<a href="#" onclick="window.parent.window.location.href = \'http://www.irishcentral.com/s?action=login&rurl=\' + encodeURIComponent(window.parent.window.location.href); return false;">Login</a>',
			'&nbsp;|&nbsp;',
			'<a href="#" onclick="window.parent.window.location.href = \'http://www.irishcentral.com/s?action=reg&rurl=\' + encodeURIComponent(window.parent.window.location.href); return false;">Become a member</a>',
			'</div>'
		].join("") ); 
		
		//..."add a comment" bug
		document.write('<div id="kSiteLife_head">Add your comment <em>(max ' + this.getCommentMaxLength() + ' characters)</em></div>');
		
		//...empty container for error messages
		document.write('<div id="kSiteLife_error">&nbsp;</div>');
		
		//...the input element (wrapped in a DIV too)
						document.write('<div><textarea id="kSiteLife_input" rows="6" cols="60" disabled="disabled"></textarea></div>');
				//...Facebook Connect stuff
		document.write('<div id="kSiteLife_facebook">');
		document.write('<input type="checkbox" id="kSiteLife_fbCheckbox" onchange="kSiteLife.onprefclick(this)" />&nbsp;<label for="kSiteLife_fbCheckbox">Send this comment to Facebook?</label>');
		document.write('</div>'); //end of the Facebook container
		
		//...the GO button
		document.write('<p><button id="kSiteLife_goBtn" disabled="disabled" onclick="kSiteLife.onsubmit()">Send Comment</button></p>');
		
		//...lastly, close up the container
		document.write('</div>');
	}
	,
	showError: function(errorType, optMessage) {
		var errorDomElem = document.getElementById("kSiteLife_error");
		switch (errorType) {
			case "nothing":
				//just clear the entire error message
				errorDomElem.innerHTML = "&nbsp;";
				break;
			case "emptyComment":
				errorDomElem.innerHTML = "Please enter a comment.";
				break;
			case "commentTooLong":
				errorDomElem.innerHTML = "Please shorten your comment to less than " + this.getCommentMaxLength() +  " characters. " +
					"Yours is " + optMessage + ".";
				break;
			case "foulness":
				//dirty word rejection
				errorDomElem.innerHTML = "Your comment contained words that are inappropriate for our community (" + optMessage + ").";
				break;
			case "daapiMsg":
				//unspecified DAAPI problem: echo the raw message
				errorDomElem.innerHTML = optMessage;
				break;
			default:
				throw new Error("Invalid errorType in showError: " + errorType);
		}
	}
	,
	onsubmitcomplete: function() {
		//"event" called when the entire comment submission is complete -- just do whatever you ordinarily would
		document.getElementById("kSiteLife_input").value = ""; //clear the input so it isn't easily repeated on reload
		
		/*navigate or reload, based on whether the redirection property was set 
		 * (it can be defaulted to a URL, or set in the kSiteLife.CommentsInput call)
		 */
		if (typeof this.redirectToUrl == "string") {
			var code = "location.href='" + this.redirectToUrl + "'";
		} else {
			var code = "location.reload()";
		}
		
		setTimeout(code, 200); //TODO: try removing the timeout (means re-testing in all browsers)
	}
}


// \\\
// "BLACK BOX" HERE -- CHANGE kSiteLife AT YOUR OWN RISK \\\
// This is the "Controller" in MVC \\\
// \\\
var kSiteLife = function() {
	//PRIVATE MEMBERS -------------------------------------------------------------------------------
	
	//used in errors and such; keep it in sync with the real variable name this object is assigned to
    var myName = "kSiteLife";
	
	var fbPrefCookieName = "gfslfb_doSend";
	
	var viewTool = null;
	var commentOnKey = null;
	var facebookEnabled = false;
	var fbOpts = {};
	var commentBody = null; //will be filled in later
	
	//space-separated list of functions to check for in the viewTool
	var requiredViewToolMemberList = "getCommentMaxLength getDaapiProcessUrl writeCommentGui showError onsubmitcomplete";
	
	function setViewTool(candidateView) {
		//validates the V in our MVC (checks the interface); sets the private property and returns true if it's OK
		var members = requiredViewToolMemberList.split(" ");
		for (var memberIdx = 0; memberIdx < members.length; memberIdx++) {
			var thisMember = members[memberIdx];
			if (typeof candidateView[thisMember] != "function") {
				return false;
			}
		}
		//if we get here, it passed validation
		viewTool = candidateView;
		return true;
	}
	
	function rememberFacebookPref(setIt) {
		//sets or deletes a cookie to remember whether the user last chose to send to FB or not
		//-- if the cookie exists, they want to post to FB by default
		function setCookie() {
			var now = new Date();
			var expiry = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate());
			var cookieSetting = fbPrefCookieName + "=1; path=/; expires=" + expiry.toGMTString();
			document.cookie = cookieSetting;
		}
		if (setIt) {
			setCookie();
		} else {
			document.cookie = fbPrefCookieName + "=; expires=Fri, 31 Dec 1999 23:59:59 GMT;";
		}
	}
	
	function readFacebookPref() {
		//simply returns true if any cookie with a matching name exists
		if ( document.cookie.indexOf(fbPrefCookieName) > -1 ) {
			return true;
		} else {
			return false;
		}
	}
	
	function submitToDaapi() {
		kSiteLifeLogger.log("Sending comment to SiteLife");
		//clear any error messages
		viewTool.showError("nothing");
		
		//TODO: lock down the form (?)
		
		var text = getCommentBody();
		var textLength = text.length;
		if ( textLength > viewTool.getCommentMaxLength() ) {
			//comment is too long
			//TODO: could make this fancier and nicer to the user (real-time feedback)
			viewTool.showError("commentTooLong", textLength);
		} else {
			if (textLength > 0) {
				//TODO: allow overrides for location and title; and add section/category support
				var commentAction = new CommentAction(commentOnKey, location.href, document.title, text);
				var request = new RequestBatch();
				request.AddToRequest(commentAction);
				request.BeginRequest( viewTool.getDaapiProcessUrl(), ondaapicomplete );
			} else {
				viewTool.showError("emptyComment");
			}
		}
	}
	
	function getCommentBody() {
		if (commentBody === null) {
			commentBody = document.getElementById("kSiteLife_input").value;
			commentBody = commentBody.replace(/(^\s+|\s+$)/g, "");
		}
		return commentBody;
	}
	
	function getDirtyWords(daapiMessage) {
		//pulls out the rejected word list from a language filter DAAPI message
		//IMPORTANT! this is tightly bound to the exact format of the DAAPI message, so it has to be maintained in sync should DAAPI change (unlikely but possible)
		daapiMessage = daapiMessage.toLowerCase();
		var startMarker = "(for example: ";
		var startAt = daapiMessage.indexOf(startMarker) + startMarker.length;
		var endAt = daapiMessage.lastIndexOf(").");
		var badWords = daapiMessage.substring(startAt, endAt);
		return badWords;
	}
	
	function ondaapicomplete(responseBatch) {
		//continues the submission process; this is the callback function passed to DAAPI
		var topMessage = responseBatch.Messages[0].Message;
		if (topMessage.toLowerCase() === "ok") {
			submitToFacebook();
		} else {
			//handle error
			if (topMessage.indexOf("language filter") > -1) {
				//dirty word filter
				viewTool.showError( "foulness", getDirtyWords(topMessage) );
			} else {
				//everything else
				viewTool.showError("daapiMsg", topMessage);
			}
		}
	}
	
	function userWantsFBPost() {
		return document.getElementById("kSiteLife_fbCheckbox").checked;
	}
	
	function submitToFacebook() {
		if ( facebookIsEnabled() && userWantsFBPost() ) {
			//get the options passed into the public method call, or use defaults
			fbOpts.articleTitle = fbOpts.articleTitle || document.title;
			fbOpts.articleUrl   = fbOpts.articleUrl   || location.href;
			fbOpts.excerpt      = fbOpts.excerpt      || "";
			fbOpts.images       = fbOpts.images       || [];
			
			kSiteLifeLogger.log("Sending comment to Facebook");
			slFB.submitArticleComment(fbOpts.articleTitle, fbOpts.articleUrl, fbOpts.excerpt, getCommentBody(), fbOpts.images, onfbcomplete);
		} else {
			onfinish();
		}
	}
	
	function onfbcomplete() {
		//FB errors tend to fail silently, so there's not much to do here
		kSiteLifeLogger.log("Facebook has called back");
		onfinish();
	}
	
	function onfinish() {
		//just passes through to the customization method
		viewTool.onsubmitcomplete();
	}
	
	function facebookIsEnabled() {
		if ( slFB && slFB.connectEnabled() ) {
			return true;
		} else {
			kSiteLifeLogger.log("connect not enabled; slFB is " + typeof slFB);
			return false;
		}
	}
	
	function initGui() {
		//finishes up the GUI based on various state conditions
		
		//check SiteLife login status (this is admittedly hacky but should still be very reliable, right?)
		var cookieString = "; " + document.cookie.toLowerCase();
		if (cookieString.indexOf("; hd=") > -1 || cookieString.indexOf("; at=") > -1) {
			setLoggedInState(true);
			//also set the Facebook Connect state
			if ( facebookIsEnabled() ) {
				document.getElementById("kSiteLife_facebook").style.display = "block";
				document.getElementById("kSiteLife_facebook").style.visibility = "visible";
				kSiteLife.setFacebookReadyState();
			}
		} else {
			setLoggedInState();
		}
	}
	
	function setLoggedInState(userIsLoggedIn) {
		/* sets up the GUI based on SiteLife anon. vs. real user
		 * userIsLoggedIn never needs to be false if the GUI writer does the HTML correctly
		 * (TODO: maybe this should be in the viewTool)
		 */
		
		if (userIsLoggedIn) {
			document.getElementById("kSiteLife_input").disabled = false;
			document.getElementById("kSiteLife_goBtn").disabled = false;
			
			//three choices with kSiteLife_login
			document.getElementById("kSiteLife_login").style.display = "none";
			//document.getElementById("kSiteLife_login").style.visibility = "hidden";
			//document.getElementById("kSiteLife_login").innerHTML = "&nbsp;";
			
			document.getElementById("kSiteLife_head").style.display = "block";
		}
	}
	
	function setFacebookLoggedInState() {
		try {
			if( slFB.isLoggedIn() ) {
				document.getElementById("kSiteLife_fbCheckbox").checked = readFacebookPref();
			} else {
				kSiteLifeLogger.log("User is not logged in to FB");
			}
		} catch(e) {
			kSiteLifeLogger.log("kSiteLife had trouble checking the FB login state");
			kSiteLifeLogger.dir(e);
		}
	}
    
	//set up a safe logger (as needed)
	if (window.console && console.firebug) {
		kSiteLifeLogger = window.console;
	} else {
		//really light custom logger
		kSiteLifeLogger = {
			log: function(msg) { if (window.console && window.console.log) {window.console.log(msg)} }
			,
			dir: function(obj) { if (window.console && window.console.dir) {window.console.dir(obj)} }
		};
	}
    
	//PUBLIC MEMBERS --------------------------------------------------------------------------------
    return {
		/**
		 * This is the only method you have to call in the HTML page. Simple sample:
		 * kSiteLife("ExternalResource", "article_123").
		 * 
		 * If Facebook Connect is used, facebookOptionsBag (an object) can be passed with any or all
		 * of the following properties to control what is sent to the Facebook feed. (Reasonable defaults
		 * are used if an option isn't specified.) Full facebookOptionsBag example:
		 * {articleTitle: "Hello World!", 
		 *  articleUrl: "http://www.pluck.com", 
		 *  excerpt: "This is an abstract of my article which will get displayed in the full Facebook feed template.", 
		 *  images: [ {src:"http://www.pluck.com/images/pluck-logo-home.gif", href:"http://www.pluck.com"} ]
		 * }
		 * 
		 * @param {String} resourceType            Always "ExternalResource"
		 * @param {String} articleId               The article ID. Must be a string.
		 * @param {String} [redirectToUrl]         Optional. The URL to navigate to after a successful comment action.
		 *                                         MUST NOT CONTAIN SINGLE-QUOTES.
		 * @param {Object} [facebookOptionsBag]    Optional. See explanation above.
		 */
        CommentsInput: function(resourceType, articleId, redirectToUrl, facebookOptionsBag) {
			if (viewTool != null) {
				//cache arguments as private properties
				if (typeof articleId != "string") {
					throw new Error(myName + ".CommentsInput can't write the user interface scaffold: you must pass a string for articleId");
				} else {
					commentOnKey = new ArticleKey( articleId.toString() ); //toString here is extra insurance to prevent DAAPI problems
				}
				if (typeof redirectToUrl == "string") {
					viewTool.redirectToUrl = redirectToUrl;
				}
				if (typeof facebookOptionsBag === "object" && facebookOptionsBag != null) {
					facebookOptions = facebookOptionsBag;
				}
				
				//if we get here, we can write the GUI
				viewTool.writeCommentGui();
				//...and init the GUI with SiteLife/Facebook login states
				initGui();
			} else {
				throw new Error(myName + ".CommentsInput can't write the user interface scaffold because it doesn't have a valid viewTool.");
			}
		}
		,
		onsubmit: function() {
			//submit the DAAPI comment action
			submitToDaapi();
		}
		,
		onprefclick: function(checkboxObj) {
			//causes a cookie to be set/deleted that tracks the user's pref on whether to send to FB by default
			rememberFacebookPref(checkboxObj.checked);
		}
		,
		setGuiWriter: function(writer) {
			var writerIsValid = setViewTool(writer);
			
			if (!writerIsValid) {
				throw new Error(myName + ".setGuiWriter was passed an invalid object. " +
					"You must assign an object with these methods defined: " + requiredViewToolMemberList);
			}
		}
		,
		setFacebookReadyState: function() {
			/* sets up the GUI for Facebook Connect by calling the init method on the 
			 * proxy object to determine if the user is logged in
			 * -- the anonymous function is a callback that handles the response
			 */
			try {
				slFB.init(setFacebookLoggedInState);
			} catch(e) {
				kSiteLifeLogger.log("kSiteLife had trouble in slFB.init");
				kSiteLifeLogger.dir(e);
			}
		}
    }
}();

//assign the View to the Controller
kSiteLife.setGuiWriter(kSiteLifeCustomizations);
