/**
 * @class HD.Login
 * @description Handles authenticating/registering and other login-related tasks within the Community platform.
 * @constructor
 * @property {object}	config
 *						The configuration for the object.
 * @property {string}	[config.siteName]
 *						The name for the active site
 * @property {boolean}	[config.showSoftLogin]
 *						Whether to enable soft-login (verifying a profile already logged in)
 * @property {boolean}	[config.updateGlobalNav]
 *						Whether to update the h-d.com global navigation with the user state
 * @property {boolean}	[config.showRegOptInFull]
 *						Whether to show addition email newsletter opt-in options
 * @property {array}	[config.regOptInChoices]
 *						An array of custom registration email newsletter opt-in options, example:
 *                      {label:'Email Option Name', elName: 'optInProperty'}
 */
HD.Login = function(config) {
	this.model = new HD.Login.Model(config);
	this.view = new HD.Login.View(this.model);
	this.controller = new HD.Login.Controller(this.model, this.view);	
	
	registerWait = new YAHOO.widget.Panel("registerWait",   
        { width: "32px",		  
          fixedcenter:true,  
          close:false,  
          draggable:false,  
          zindex:200000, 
          modal:true,
          underlay: 'none',
          visible:false 
        }  
    );
	
	registerWait.setHeader('');
	registerWait.setBody('<img src="' + comMediaBaseUrl + '/hd/hd_login/assets/skins/hd/loading.gif"/>');  
	registerWait.render(document.body);
};

/** The default name for an anonymous user */
HD.Login.ANONYMOUS_USER_NAME = "anonymousUser";

/**
 * The data model for HD.Login
 * @param {object}	config The configuration for the model.
 */
HD.Login.Model = function(config) {	
	this.config = config;
	this.config.showSoftLogin = this.config.showSoftLogin || false;
	this.config.updateGlobalNav = this.config.updateGlobalNav || true;
	this.config.showRegOptInFull = this.config.showRegOptInFull || false;
	this.config.regOptInChoices = this.config.regOptInChoices || [{label:'Harley-Davidson News', elName: 'hdInterest', description: 'Get the latest news on motorcycles, events, special offers and more.', checkedByDefault: false},{label:'Harley-Davidson Museum News', elName: 'optInMuseumEmail', description: 'Get the latest on what\'s happening at the Harley-Davidson Museum&#174; in Milwaukee.', checkedByDefault: false},{label:'Women\'s Roadmap to Riding Emails', elName: 'womensRoadMapOptIn', description: 'Get motivational reminders when you use the <a href="/en_US/Content/Pages/women-riders/roadmap/roadmap.html">Harley-Davidson&#174; Roadmap to Riding</a>.', checkedByDefault: false}];
	this.events = {
		registered : new HD.util.Event("registered"),
		authenticated : new HD.util.Event("authenticated"),
		passwordReset : new HD.util.Event("passwordReset"),
		screenNameCreated: new HD.util.Event("screenNameCreated"),
		subscriptionsUpdated: new HD.util.Event("subscriptionsUpdated")
	};
	/** Whether login is ready to process login requests */
	this.ready = true;
	/** A cache of the active IL ticket for the logged in user */
	this.ilTicket = null;
	this.queue = [];
	
	this.observers = [];
};

HD.Login.Model.prototype = {
	ANONYMOUS : "anonymous",
	REGISTERED : "registered",
	AUTHENTICATED : "authenticated",
	
	/** An array of host expressions that require HTTPS */
	publicSites : [
		// /^https?:\/\/[^/]*?\.harley-davidson\.com.*/,
		/^https?:\/\/www\.harley-davidson\.com.*/,
		/^https?:\/\/www\.buell\.com.*/,
		/^https?:\/\/members\.hog\.com.*/
	],
	
	/** The base profile url */
	profileUrl: '',
	
	/**
	 * Queues a function call to be executed when login is ready (for isUserLoggedIn)
	 * @param {function} request The function to execute when ready
	 */
	queueRequest : function(request) {
		// Add request functions to the queue if necessary.
		if (!this.ready) {
			this.queue.push(request);
		}
		else {
			request();
		}
	},
	
	/**
	 * Executes the function call queue when login is ready (for isUserLoggedIn)
	 * @param {object} userResponse The user response to pass to queued functions
	 */
	processQueue : function(userResponse) {
		// Execute every function in the queue.
		if (this.ready) {
			while (this.queue.length > 0) {
				this.queue.shift()(userResponse);
			}
		}
	},
	
	/**
	 * Changes the active DWR request method to allow for cross-domain HTTPS requests
	 * @param {boolean} [isRegister] Whether the request is a registration request
	 */
	setRpc : function(isRegister) {
		this.rpcType = DWREngine._rpcType;
		this.path = UserService._path;
		
		if(this.isPublicSite() && (!isRegister || !YAHOO.env.ua.ie || YAHOO.env.ua.ie >= 7)) {
			var serverUrl = HD.util.Common.getServerUrl();
			if(serverUrl.indexOf("https") != 0) {
				serverUrl = serverUrl.replace(/http/, "https");
			}
			UserService._path = serverUrl + UserService._path;
			DWREngine.setMethod(DWREngine.ScriptTag);
		}
	},
	
	/**
	 * Reverts the active DWR request method to the default
	 */
	resetRpc : function() {
		DWREngine.setMethod(this.rpcType);
		UserService._path = this.path;
	},
	
	/**
	 * Checks the list of public sites to see if the active host name should get HTTPS treatment
	 */
	isPublicSite : function() {
		var serverUrl = HD.util.Common.getServerUrl();
		
		for(var i = 0, len = this.publicSites.length; i < len; i++) {
			if(serverUrl.match(this.publicSites[i])) {
				return true;
			}
		}
		
		return false;
	},
	
	/**
	 * Caches the IL ticket for a user response for logged in users (to be leveraged in other services)
	 * @param {object} userResponse The response object to retrieve an IL ticket from
	 */
	cacheUser : function(userResponse) {
		this.ilTicket = (userResponse.loggedInLevel > 1 && userResponse.user && userResponse.user.ilTicket) || null;
	},
	
	/**
	 * Clears the active cached user IL ticket
	 */
	clearUser : function() {
		this.ilTicket = null;
	},
	
	/** 
	 * Retrieves the active user's logged in status
	 * @param {function} callback A callback function to pass the user response after it is ready
	 */
	getUser : function(callback) {
		this.notifyObservers("getUser_Start");
		var cb = this;
		var userRequest = {};
		this.setFilters(userRequest);
		UserService.getLoggedInStatus(userRequest, function(userResponse){
			cb.cacheUser(userResponse);
			if (callback) callback(userResponse);
			cb.notifyObservers.call(cb, "getUser_Finish", userResponse);
		});
	},
	
	/**
	 * Initializes the login object
	 * @param {function} callback The callback to be executed after initialization
	 * @param {boolean} [forceLogin] Whether the user must be logged in to perform actions on the active page
	 */
	init : function(callback, forceLogin) {
		// Handle forced logins
		this.forceLogin = !!forceLogin;
		this.ready = !this.forceLogin || false;
		if (this.forceLogin) {
			var userCallback = callback || function() {};
			callback = function() {
				userCallback();
				// Keep retrying until a user is found.
				(function tryGetUser() {
					cb.getUser(function(userResponse) {
						if (userResponse.loggedInLevel > 1) {
							cb.ready = true;
							cb.processQueue(userResponse);
						}
						else {
							// Try again.
							setTimeout(function() {
								tryGetUser();
							}, 250);
						}
					});
				})();
			};
		}
		
		var cb = this;
		this.notifyObservers("loginInit_Start");
		UserService.getIlCookies(function(response){
			var returnurl = HD.util.Common.getServerUrl() + "/Community/services/ILInit/?secret=" + HD.util.Common.timestamp();
			var url = response.profile +"/cpapp/auth/ILTicket.h-d?nologin=1&ret_uri=" + escape(returnurl);
			cb.profileUrl = response.profile;
			HD.util.Common.addImage(url, callback || null);
			cb.notifyObservers("loginInit_Finish", HD.merge(response, { returnUrl: returnurl }));
		});
	},
	
	/**
	 * Authenticates a user
	 * @param {object} user A collection of properties used to authenticate
	 */
	authenticate : function(user) {
		this.setRpc();
		var cb = this;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.authenticate(userRequest, function(userResponse){
			cb.handleAuthenticate.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
		this.resetRpc();
	},
	
	/**
	 * Handles the response of an authentication request
	 * @param {object} user The user response from the service
	 */
	handleAuthenticate : function(user) {
		this.cacheUser(user);
		if(user!=null){
			if ((user.user.screenName == null) && (user.user.ilTicket != null)){
				this.events.screenNameCreated.fire(user);
			} else {
				this.events.authenticated.fire(user);
			}
		}		
	},
	
	/**
	 * Registers a user
	 * @param {object} user A collection of properties used to register
	 */
	register : function(user) {
		this.setRpc(true);
		var cb = this;
		var u = user;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.register(userRequest, function(userResponse){
			cb.handleRegister.call(cb, userResponse);
			registerWait.hide();
		});
		var scrollable = HD.get(HD.Login.Overlay.REGISTER.content.hooks.BODY);
		registerWait.show();
		this.resetRpc();
	},
	
	/**
	 * Handles the response of an registration request
	 * @param {object} user The user response from the service
	 */
	handleRegister : function(userResponse) {
		this.cacheUser(userResponse);
		this.events.registered.fire(userResponse);
	},
	
	/**
	 * Resets the password for a user
	 * @param {object} user A collection of properties representing the user
	 */
	resetPassword : function(user) {
		this.setRpc();
		var cb = this;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.resetPassword(userRequest, function(userResponse){
			cb.handleResetPassword.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
		this.resetRpc();
	},
	
	/**
	 * Handles the response of an resetPassword request
	 * @param {object} user The user response from the service
	 */
	handleResetPassword: function(userResponse) {
		this.cacheUser(userResponse);
		this.events.passwordReset.fire(userResponse);
	},

	/**
	 * Checks if a screen name is available for a user in the registration form
	 * @param {object} user A collection of properties used to check the screen name
	 */
	checkScreenName : function(user) {
		var cb = this;
		var u = user;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.checkScreenname(userRequest, function(userResponse){
			cb.handleCheckScreenName.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
	},
	
	/**
	 * Handles the response of an check screen name within registration request
	 * @param {object} user The user response from the service
	 */
	handleCheckScreenName : function(userResponse) {	
		this.cacheUser(userResponse);	
		this.events.registered.fire(userResponse);		
	},
	
	/**
	 * Checks if a screen name is available for a user
	 * @param {object} user A collection of properties used to check the screen name
	 */
	checkScreenNameCreate : function(user) {
		var cb = this;
		var u = user;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.checkScreenname(userRequest, function(userResponse){
			cb.handleCheckScreenNameCreate.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
	},
	
	/**
	 * Handles the response of an check screen name request
	 * @param {object} user The user response from the service
	 */
	handleCheckScreenNameCreate : function(userResponse) {	
		this.cacheUser(userResponse);	
		this.events.screenNameCreated.fire(userResponse);		
	},
	
	/**
	 * Creates a screen name for a user if the screen name is available
	 * @param {object} user A collection of properties used to set the screen name
	 */
	createScreenName : function(user) {
		var cb = this;
		var u = user;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.createScreenName(userRequest, function(userResponse){
			cb.handleCreateScreenName.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
	},
	
	/**
	 * Handles the response of an screen name creation request
	 * @param {object} user The user response from the service
	 */
	handleCreateScreenName : function(userResponse) {	
		this.cacheUser(userResponse);	
		this.events.screenNameCreated.fire(userResponse);	
	},
	
	/**
	 * Manages the email newsletter subscriptions for a given user
	 * @param {object} user A collection of properties used to set the updated subscriptions
	 */
	manageSubscriptions : function(user) {
		var cb = this;
		var u = user;
		var userRequest = {user:user};
		this.setFilters(userRequest);
		UserService.emailSignUp(userRequest, function(userResponse){
			cb.handleManageSubscriptions.call(cb, userResponse);
			registerWait.hide();
		});
		registerWait.show();
	},
	
	/**
	 * Handles the response of an subscriptions management request
	 * @param {object} user The user response from the service
	 */
	handleManageSubscriptions : function(userResponse) {	
		this.cacheUser(userResponse);	
		this.events.subscriptionsUpdated.fire(userResponse);	
	},
	
	/**
	 * Sets the filters for a given login request
	 * @param {object} request The request to add filters to
	 */
	setFilters : function(request) {
		request.siteName = this.config.siteName;
	},
	
	/**
	 * Checks whether a user is logged in
	 * @param {function} callback The function to pass the user response object to
	 * @param {boolean} [validateUser] Whether to automatically handle prompting for creating a screen name/authentication
	 */
	isUserLoggedIn : function(callback, validateUser) {
		var userRequest = {}, cb = this;
		this.setFilters(userRequest);
		this.queueRequest(function(response) {
			if (!!response) {
				cb.cacheUser(response);
				callback(response);
			}
			else {
				UserService.getLoggedInStatus(userRequest, function(userResponse){
					cb.cacheUser(userResponse);
					
					// Continue for valid users or when validation is skipped.
					if (!validateUser || userResponse.loggedInLevel > 1) {
						callback(userResponse);	
					}
					// Handle automation for non-primed users.
					else {
						var observer = {
							update : function(eventName, eventData) {
								if (eventName == "logged_In") {
									callback(eventData);	
									cb.removeObserver(this);
									HD.util.Common.getObservable().removeObserver(this);
								} else if (eventName == "overlayClose_Click") {
									cb.removeObserver(this);
									HD.util.Common.getObservable().removeObserver(this);
								}
							}
						};
						HD.util.Common.getObservable().addObserver(observer);
						cb.addObserver(observer);
			
						if (userResponse.loggedInLevel > 0 && window.login) {
							login.view.promptCreateScreenName();
						}
						else if (window.login) {
							login.view.promptAuthentication();
						}
					}
				});
			}
		});
	}
};

HD.extend(HD.Login.Model, [HD.util.Observable]);

/**
 * The view for HD.Login
 * @param {object}	model The data model for the view.
 */
HD.Login.View = function(model){
	/** A local reference the collection of overlays */
	this.overlays = HD.Login.Overlay;
	this.model = model;
	this.config = model.config;
	
	this.AUTHENTICATE_FORM_ID = "hdAuthenticateForm";
	this.AUTHENTICATE_SUBMIT_ID = "hdAuthenticateSubmit";
	this.REGISTER_FORM_ID = "hdRegisterForm";
	this.REGISTER_SUBMIT_ID = "hdRegisterSubmit";
	this.CHECK_SCREENNAME_ID = "hdCheckScreenname";
	this.SUBSCRIPTIONS_ID = "hdSubscriptionsForm";
	
	/** Login events */
	this.events = {
		registerClick : new HD.util.Event("registerClick"),
		authenticateClick : new HD.util.Event("authenticateClick"),
		resetPasswordClick : new HD.util.Event("resetPasswordClick"),
	    screennameCheckClick : new HD.util.Event("screennameClick"),
	    checkSNCreateClick : new HD.util.Event("checkSNCreateClick"),
		createScreenNameClick : new HD.util.Event("createScreenNameClick"),
		manageSubscriptionsClick : new HD.util.Event("manageSubscriptionsClick")
	};
	
	var cb = this;
	/** Login event handlers */
	this.model.events.authenticated.subscribe(function(userResponse){cb.handleAuthenticated.call(cb, userResponse)});
	this.model.events.registered.subscribe(function(userResponse){cb.handleRegistered.call(cb, userResponse)});
	this.model.events.passwordReset.subscribe(function(userResponse) {cb.handleResetPassword.call(cb, userResponse)});
	this.model.events.screenNameCreated.subscribe(function(userResponse){cb.handleScreenNameCreated.call(cb, userResponse)});
	this.model.events.subscriptionsUpdated.subscribe(function(userResponse){cb.handleManageSubscriptions.call(cb, userResponse)});
	
	this.observers = [];
	
	this.model.addObserver(this);
};

HD.Login.View.prototype = {
	/**
	 * Listen for login events to process
	 * @param {string} eventName The event that was thrown
	 * @param {object} eventData Contextual event data
	 */
	update : function(eventName, eventData) {
		var cb = this;
		if(eventName == "getUser_Finish") {
			cb.loginStatus.call(cb, eventData);
		}
	},
	
	/** 
	 * Notifies observers of a change in the login status
	 * @param {object} userResponse The user response to notify observers with
	 */
	loginStatus : function(userResponse) {
		var eventName = "notLogged_In";
		
		if(userResponse != null){
			if(userResponse.loggedInLevel > 1){
				eventName = "logged_In";
			}
		}
		
		this.model.notifyObservers(eventName, userResponse);
	},
	
	/**
	 * Shows a login overlay given a overlay config
	 * @param {object} formConfig The configuration for the specified overlay
	 * @param {object} [userResponse] A user response to pass along to the overlay template
	 */
	showForm : function(formConfig, userResponse) {
		var cb = this;
		var fConfig = formConfig.content;
		var timestamp = HD.util.Common.timestamp();
		registerWait.show();			
		var overlayEl = HD.util.Common.showOverlay(formConfig.getHtml(HD.merge(userResponse || {}, { profile: this.model.profileUrl || '', showRegFullOptIn:  this.config.showRegOptInFull, regOptInChoices: this.config.regOptInChoices})), fConfig.title, fConfig.width, fConfig.height);			
		cb.setupListeners.call(cb, overlayEl, fConfig);
		registerWait.hide();
	},
	
	/**
	 * Shows a login overlay given a overlay config scrolled to the bottom
	 * @param {object} formConfig The configuration for the specified overlay
	 * @param {object} [userResponse] A user response to pass along to the overlay template
	 */
	showFormScrolled : function(formConfig, userResponse) {
		var cb = this;
		var fConfig = formConfig.content;
		var timestamp = HD.util.Common.timestamp();
		registerWait.show();
		
		var overlayEl = HD.util.Common.showOverlay(formConfig.getHtml(HD.merge(userResponse || {}, { profile: this.model.profileUrl || '', showRegFullOptIn:  this.config.showRegOptInFull, regOptInChoices: this.config.regOptInChoices })), fConfig.title, fConfig.width, fConfig.height);
		cb.setupListeners.call(cb, overlayEl, fConfig);
		
		var scrollable = HD.get(fConfig.hooks.BODY);
		var offset = HD.get(fConfig.hooks.FORM_BOTTOM).offsetTop;
		scrollable.scrollTop = offset;
		registerWait.hide();
	},
	
	/**
	 * Attaches event listeners to a login overlay
	 * @param {HTMLElement} overlayEl The container element for the overlay 
	 * @param {object} fConfig The configuration for the specified overlay
	 */
	setupListeners : function(overlayEl, fConfig) {
		var formEl = HD.getByClass(fConfig.hooks.FORM, 'form', overlayEl)[0] || null;
		var cb = this;
		
		if(formEl != null){
			formEl.onsubmit = function() {
				HD.addClass(formEl, "hdFormLoading");
				cb[fConfig.callback].call(cb, formEl);
				return false;
			};
			
			HD.getByClass(fConfig.hooks.OPT_IN, "*", formEl, function(el) {
    			el.onclick = function(){
    				
    				HD.getByClass(fConfig.hooks.OPT_IN_SUB, "*", formEl, function(subEl){
    					
    					if(!el.checked){
        					subEl.checked = false;
        					subEl.disabled = true;
        					
        				}else{
        					subEl.disabled = false;
        				}
    				});
    				
    				
    			};
    		});
		}		
		
		var closeEls = HD.getByClass(fConfig.hooks.SHOW_REGISTER || "hdShowRegister", "*", overlayEl);
		if(closeEls != null && closeEls.length > 0) {			
			closeEls[0].onclick = function() {	
				cb.promptRegistration.call(cb);
			};
		}
		
		closeEls = HD.getByClass(fConfig.hooks.LOGOUT || "hdLogout", "*", overlayEl);		
		if(closeEls != null && closeEls.length > 0) {
			closeEls[0].onclick = function() {				
				cb.logoutUser.call(cb);
				HD.util.Common.hideOverlay();
			};
		}
	},
	
	/**
	 * Prompts the user to set a screen name
	 */
	promptCreateScreenName : function(user){
		HD.util.Analytics.track(this.analyticsScreenNameString());
		this.showForm(this.overlays.SCREENNAME, user || null);
	},
	
	/**
	 * Handles the screen name setting submission
	 * @param {HTMLElement} form The form element containing user data
	 */
	onScreenNameSubmit : function(form) {		
		var user = this.collectUserData(form);		
		if ((form.checkingScreenname.value == "true")){			
			this.events.checkSNCreateClick.fire(user);
		} else {			
			this.events.createScreenNameClick.fire(user);
		}
	},	
	
	/**
	 * Prompts the user to manage email subscriptions
	 */
	promptSubscriptions : function(user) {
		HD.util.Analytics.track(this.analyticsSubscriptionsString());
		this.showForm(this.overlays.SUBSCRIPTIONS, user || null);
	},
	
	/**
	 * Handles the email subscription management submission
	 * @param {HTMLElement} form The form element containing user data
	 */
	onSubscriptionsSubmit : function(form) {		
		var user = this.collectUserData(form);			
		this.events.manageSubscriptionsClick.fire(user);
	},
	
	/**
	 * Prompts the user to authentication
	 */
	promptAuthentication : function() {
		HD.util.Analytics.track(this.analyticsAuthenticateString());
		if(this.config.showSoftLogin && this.isSoftLoggedIn()){
			this.showForm(this.overlays.AUTHENTICATE_SOFT);
		}
		else{
			this.showForm(this.overlays.AUTHENTICATE);
		}
	},
	
	/**
	 * Handles the authentication submission
	 * @param {HTMLElement} form The form element containing user data
	 */
	onAuthenticationSubmit : function(form) {
		var user = this.collectUserAuthenticateData(form);
		this.events.authenticateClick.fire(user);
	},
	
	/**
	 * Prompts the user to register
	 */
	promptRegistration : function() {
		HD.util.Analytics.track(this.analyticsRegisterString());
		this.showForm(this.overlays.REGISTER);
	},
	
	/**
	 * Logs a user out
	 * @param {function} callback A callback to execute after logging the user out
	 */
	logoutUser : function(callback) {
		UserService.clearSessionUserInfo();
		UserService.logoutUser();
		this.model.clearUser();
		this.loginStatus();
//		registerWait.hide();
		var cb = this;
		UserService.getIlCookies(function(response){
			var timestamp = HD.util.Common.timestamp();
			HD.util.Common.addImage(response.hdLogout +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.buellLogout +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.hogLogout +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.rideplannerLogout +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.profile + "/cpapp/ilCookie/delete/", callback);
			// TEMPORARY DELAYED CALLBACK UNTIL /cpapp/ilCookie/* RETURNS AN IMAGE:
			setTimeout(function() {
				if (callback) {
					callback();
				}
			}, 2000);
		});
	},
	
	/**
	 * Handles the registration submission
	 * @param {HTMLElement} form The form element containing user data
	 */
	onRegistrationSubmit : function(form) {		
		var user = this.collectUserData(form);		
		if ((form.checkingScreenname.value == "true")){			
			this.events.screennameCheckClick.fire(user);
		} else {			
			this.events.registerClick.fire(user);
		}		
	},
	
	/**
	 * Handles the resetPassword submission
	 * @param {HTMLElement} form The form element containing user data
	 */
	onResetPasswordSubmit : function(form) {
		var user = this.collectUserData(form);
		this.events.resetPasswordClick.fire(user);
	},

	/**
	 * Prompts the user to logout
	 */
	showLogoutOverlay : function() {		
		this.showForm(this.overlays.LOGOUT);
	},
	
	/**
	 * Handles the authentication response
	 * @param {object} userResponse The user response from the authentication service
	 */
	handleAuthenticated : function(userResponse) {
		var cb = this;
		var user = userResponse.user;
		if (userResponse.resetPassword ) {
			// User is authenticated, but they must now reset their password...
			// Null out the error message since we don't want to show
			// an error initally on the reset password screen/form
			userResponse.errors = null;
			// Pass in the current password as the "old" password
			// because we are about to change it...
			userResponse.user.oldPassword = userResponse.user.password;
			this.showForm(this.overlays.RESET_PASSWORD, userResponse);
		} else if(userResponse.loggedInLevel == 0) {
			if(this.config.showSoftLogin && this.isSoftLoggedIn()){
				this.showForm(this.overlays.AUTHENTICATE_SOFT, userResponse);
			}
			else{
				this.showForm(this.overlays.AUTHENTICATE, userResponse);
			}
		} else if(userResponse.loggedInLevel == 1){
			this.promptCreateScreenName(userResponse);			
		} else {
			HD.util.Common.hideOverlay();
			cb.loginStatus(userResponse);
			cb.writeCookies(user, function() { cb.updateGlobalNav(); });
			if(this.config.showSoftLogin && this.isSoftLoggedIn()){
				HD.util.Analytics.track(this.analyticsAuthenticateVerifiedString(user));
			}
			else {
				HD.util.Analytics.track(this.analyticsAuthenticateConfirmString(user));
			}
		}
	},
	
	/**
	 * Updates the h-d.com global navigation with updated user data
	 * @param {function} [callback] A callback to execute after updating the global navigation
	 */
	updateGlobalNav : function(callback){
		if (this.config.updateGlobalNav && HD && HD.Nav){
			setTimeout(function(){
				HD.Nav.toggleLoginState();
				if (callback){
					callback();
				}
			}, 2000);
		}
	},
	
	/**
	 * Closes the active overlay and prompts the user to authenticate
	 */
	closeAndAuthenticate : function(){
		HD.util.Common.hideOverlay();
		this.promptAuthentication();
	},	
	
	
	/**
	 * Prompts the user to authenticate after the h-d.com global navigation is updated
	 */
	updateNavAndAuthenticate : function(){
		var cb = this;
		this.updateGlobalNav(function() { cb.closeAndAuthenticate(); });
	},
	
	/**
	 * Handles the registration response
	 * @param {object} userResponse The user response from the registration service
	 */
	handleRegistered : function(userResponse) {
		var user = userResponse.user;
		var errors = userResponse.errors;
		var screenNameCheck = userResponse.screenNameCheck;
		var cb = this;
		if((errors == null || errors.length < 1) && !screenNameCheck) {
			HD.util.Common.hideOverlay();
			HD.util.Analytics.track(this.analyticsRegisteredString(user));
			cb.showForm(this.overlays.CONFIRMATION, userResponse);
			cb.loginStatus(userResponse);
			cb.writeCookies(user, function() { cb.updateGlobalNav(); });
		} else if(screenNameCheck){			
			this.showFormScrolled(this.overlays.REGISTER, userResponse);
		} else {
			this.showForm(this.overlays.REGISTER, userResponse);
		}
	},
	
	/**
	 * Handles the reset password response
	 * @param {object} userResponse The user response from the update service
	 */
	handleResetPassword: function(userResponse) {
		var user = userResponse.user;
		var errors = userResponse.errors;
		if((errors == null || errors.length < 1)) {
			// No errors - so now we proceed as usual
			this.handleAuthenticated( userResponse );
			//HD.util.Common.hideOverlay();
			//HD.util.Analytics.track(this.analyticsRegisteredString(user));
		} else {
			// There were errors, so we stay on the current form and display them
			this.showForm(this.overlays.RESET_PASSWORD, userResponse);
		}
	},

	/**
	 * Handles the screen name creation response
	 * @param {object} userResponse The user response from the screen name creation service
	 */
	handleScreenNameCreated : function(userResponse) {
		var user = userResponse.user;
		var errors = userResponse.errors;
		var screenNameCheck = userResponse.screenNameCheck;
		if((errors == null || errors.length < 1) && !screenNameCheck && (user != null) && (user.screenName != null)) {
			HD.util.Common.hideOverlay();	
			this.showForm(this.overlays.SN_CONFIRMATION, userResponse);
			HD.util.Analytics.track(this.analyticsScreenNameCreatedString(user));
			this.loginStatus(userResponse);
		} else {
			this.promptCreateScreenName(userResponse);	
		}
	},
	
	/**
	 * Handles the email subscription management response
	 * @param {object} userResponse The user response from the email subscription management service
	 */
	handleManageSubscriptions : function(userResponse) {
		var user = userResponse.user;
		var errors = userResponse.errors;
		var screenNameCheck = userResponse.screenNameCheck;
		var cb = this;
		if((errors == null || errors.length < 1) && !screenNameCheck && !!user) {
			HD.util.Common.hideOverlay();
			HD.util.Analytics.track(this.analyticsSubscriptionsUpdatedString(user));
			cb.showForm(this.overlays.UPDATED, userResponse);
			cb.loginStatus(userResponse);
			cb.writeCookies(user, function() { cb.updateGlobalNav(); });
		} else {
			this.showForm(this.overlays.SUBSCRIPTIONS, userResponse);
		}
	},
	
	/**
	 * Grabs all user-related data from the active login overlay
	 * @param {HTMLElement} form The form to grab user data from
	 * @returns {object} An object containing cleaned up values for a user service request
	 */
	collectUserData : function(form) {
		var user = {};		
		if(form.hdwcPassword != null) {
			user.password = form.hdwcPassword.value;
		}
		if(form.hdwcPassword2 != null) {
			user.passwordConfirm = form.hdwcPassword2.value;
		}
		if (form.hdwcOldPassword != null ) {
			user.oldPassword = form.hdwcOldPassword.value;
		}
		
		if (form.hdwcIlTicket != null ) {
			user.ilTicket = form.hdwcIlTicket.value;
		}

		if(form.hdwcFirstName != null) {
			user.firstName = form.hdwcFirstName.value;
		}
		if(form.hdwcMiddleName != null) {
			user.middleName = form.hdwcMiddleName.value;
		}
		if(form.hdwcLastName != null) {
			user.lastName = form.hdwcLastName.value;
		}
		if(form.hdwcEmailAddress != null) {
			//user.email = form.hdwcEmailAddress.value;
			//Username will be the same as the email address going forward 
			user.userName = form.hdwcEmailAddress.value;
		}
		if (form.hdwcUserName != null ) {
			//... unless we are on the resetPassword form
			user.userName = form.hdwcUserName.value;
		}
		if(form.hdwcSelectMonth != null && 
				form.hdwcSelectDay != null && form.hdwcSelectYear != null) {
			// Must format the date to a strick MM/yy/dd otherwise the validation will fail
			var tmpDob = (form.hdwcSelectMonth.value && ((form.hdwcSelectMonth.value.length < 2 ? '0' : '') + form.hdwcSelectMonth.value)) + "/";
			tmpDob += (form.hdwcSelectDay.value && ((form.hdwcSelectDay.value.length < 2 ? '0' : '') + form.hdwcSelectDay.value)) + "/";
			tmpDob += form.hdwcSelectYear.value;
			
			user.dob = tmpDob;	
		}
		if(form.optIn != null) {
			user.optIn = form.optIn.checked;
		}
		HD.getByClass(HD.Login.Overlay.REGISTER.content.hooks.OPT_IN_SUB, 'input', form, function(inputEl) {
			if (inputEl.type != "hidden"){
				user[inputEl.name] = inputEl.checked;
			}
			else{
				user[inputEl.name] = inputEl.value;
			}
		});	
		if(form.screenName != null) {
			user.screenName = form.screenName.value;				
		}
		if(form.hdwcZip != null) {
			user.zip = form.hdwcZip.value;				
		}
		if(form.gender != null) {
			user.gender = form.gender.value;		
		}
		if(form.ownABike != null) {
			var ownABike = null;
			var ownEl = form.ownABike;
			
			for (var i=0; i< ownEl.length; i++){
				if (ownEl[i].checked){
					ownABike = ownEl[i].value;
					break;
				}
			}
			
			user.ownABike = ownABike;		
		}
		if(form.makeOfBikeOwnedOrOwn != null) {
			user.makeOfBikeOwnedOrOwn = form.makeOfBikeOwnedOrOwn.value;		
		}
		if(form.haveMotorcycleLicense != null) {
			var hasLicense = null;
			var licenseEl = form.haveMotorcycleLicense;
			
			for (var i=0; i< licenseEl.length; i++){
				if (licenseEl[i].checked){
					hasLicense = licenseEl[i].value;
					break;
				}
			}
			
			user.haveMotorcycleLicense = hasLicense;			
		}
		return user;
	},
	
	/**
	 * Grabs all user authentication data from the active login overlay
	 * @param {HTMLElement} form The form to grab user data from
	 * @returns {object} An object containing cleaned up values for a user service request
	 */
	collectUserAuthenticateData : function(form) {
		var user = {};		
		if(form.password != null) {
			// IL doesn't have functionality to validate passwords - throwing in a hook of our own
			user.password = form.password.value;				
		}
		
		if(form.email != null) {
			//user.email = form.email.value;
			//Username will be the same as the email address going forward 
			user.userName = form.email.value;
		}
		return user;
	},
	
	/**
	 * Writes login cookies for integrated login
	 * @param {object} user The user response to write cookies for
	 * @param {function} [callback] The callback to execute when the IL session is ready
	 */
	writeCookies : function(user, callback) {	
		var cb = this;
		cb.user = user;
		UserService.getIlCookies(function(response){
			var timestamp = HD.util.Common.timestamp();
			HD.util.Common.addImage(response.hdLogin +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.buellLogin +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.hogLogin +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.rideplannerLogin +"?timestamp=" + timestamp);
			HD.util.Common.addImage(response.profile + "/cpapp/ilCookie/add/" + cb.user.ilTicket +"/" +cb.user.userName +"/" +cb.user.screenName, callback);
			// TEMPORARY DELAYED CALLBACK UNTIL /cpapp/ilCookie/* RETURNS AN IMAGE:
			setTimeout(function() {
				if (callback) {
					callback();
				}
			}, 2000);
		});
	},
	
	/**
	 * Determines whether the user is currently soft-logged in (logged into h-d.com but not Community)
	 */
	isSoftLoggedIn : function(){
		var hasNickname = false;
		var userNameCookie = null;
		try {
			userNameCookie = YAHOO.util.Cookie.get('WC_PROFILEUSER_NICKNAME');
		}
		catch (e) {
			if (document.cookie.indexOf("WC_PROFILEUSER_NICKNAME=") > -1) {
				userNameCookie = decodeComponent(document.cookie.substring(document.cookie.indexOf("WC_PROFILEUSER_NICKNAME=") + "WC_PROFILEUSER_NICKNAME=".length, document.cookie.indexOf(";", document.cookie.indexOf("WC_PROFILEUSER_NICKNAME=") + "WC_PROFILEUSER_NICKNAME=".length)));
			}
		}
		if(userNameCookie){
			hasNickname = true;
		}
		return hasNickname;
	},
	
	/**
	 * Logs the user out and prompts for re-authentication
	 */
	softLogout : function(){
		var cb = this;
		this.logoutUser(function() { cb.updateNavAndAuthenticate(); });
	},
	
	/** Analytics event after authentication prompt */
	analyticsAuthenticateString : function() {
		return "default_analyticsAuthenticateString";
	},
	
	/** Analytics event after registration prompt */
	analyticsRegisterString : function() {
		return "default_analyticsRegisterString";
	},
	
	/** Analytics event after registration confirmed */
	analyticsRegisteredString : function() {
		return "default_analyticsRegisteredString";
	},
	
	/** Analytics event after screen name prompt */
	analyticsScreenNameString : function() {
		return "default_analyticsScreenNameString";
	},
	
	/** Analytics event after screen name created */
	analyticsScreenNameCreatedString : function() {
		return "default_analyticsScreenNameCreatedString";
	},
	
	/** Analytics event after email subscriptions prompt */
	analyticsSubscriptionsString : function() {
		return "default_analyticsSubscriptionsString";
	},
	
	/** Analytics event after email subscriptions updated */
	analyticsSubscriptionsUpdatedString : function() {
		return "default_analyticsSubscriptionsUpdatedString";
	},
	
	/** Analytics event after authentication confirmed */
	analyticsAuthenticateConfirmString : function() {
		return "default_analyticsAuthenticateConfirmString";
	},
	
	/** Analytics event after authentication verified (soft-logged in) */
	analyticsAuthenticateVerifiedString : function() {
		return "default_analyticsAuthenticateVerifiedString";
	}	
	
};

HD.extend(HD.Login.View, [HD.util.Observable]);

/**
 * The controller for HD.Login
 * @param {object}	model The data model for the controller.
 * @param {object}	view The view for the controller.
 */
HD.Login.Controller = function(model, view){
	this.model = model;
	this.view = view;
	
	var cb = this;
	this.view.events.registerClick.subscribe(function(user){cb.handleRegister.call(cb, user)});
	this.view.events.authenticateClick.subscribe(function(user){cb.handleAuthenticate.call(cb, user)});
	this.view.events.resetPasswordClick.subscribe(function(user){cb.handleResetPassword.call(cb, user)});
	this.view.events.screennameCheckClick.subscribe(function(user){cb.handleCheckScreenname.call(cb, user)});
	this.view.events.checkSNCreateClick.subscribe(function(user){cb.handleCheckSNCreate.call(cb, user)});
	this.view.events.createScreenNameClick.subscribe(function(user){cb.handleCreateScreenName.call(cb, user)});
	this.view.events.manageSubscriptionsClick.subscribe(function(user){cb.handleManageSubscriptions.call(cb, user)});
	
};

HD.Login.Controller.prototype = {
	/** Prompts the user for authentication */
	authenticateUser : function() {
		this.view.promptAuthentication();
	},
	
	/** Prompts the user for registration */
	registerUser : function() {
		this.view.promptRegistration();
	},
	
	/**
	 * Handles registration event
	 * @param {object} user The user response from registration
	 */
	handleRegister : function(user) {
		this.model.register(user);
	},
	
	/**
	 * Handles authentication event
	 * @param {object} user The user response from authentication
	 */
	handleAuthenticate : function(user) {
		this.model.authenticate(user);
	},
	
	/**
	 * Handles resetPassword event
	 * @param {object} user The user response from resetPassword
	 */
	handleResetPassword : function(user) {
		this.model.resetPassword(user);
	},

	/**
	 * Handles screen name check event
	 * @param {object} user The user response from screen name check
	 */
	handleCheckScreenname : function(user) {
		this.model.checkScreenName(user);
	},
	
	/**
	 * Handles screen name check (from registration) event
	 * @param {object} user The user response from screen name check (from registration)
	 */
	handleCheckSNCreate : function(user) {
		this.model.checkScreenNameCreate(user);
	},
	
	/**
	 * Handles screen name creation event
	 * @param {object} user The user response from screen name creation
	 */
	handleCreateScreenName : function(user) {
		this.model.createScreenName(user);
	},
	
	/**
	 * Handles email subscriptions management event
	 * @param {object} user The user response from subscriptions updated
	 */
	handleManageSubscriptions : function(user) {
		this.model.manageSubscriptions(user);
	}
};

HD.register('hd_login_widget', 'HD.Login', {version: "1.0", build: "1"});
