dojo.provide("UI.Widgets.User.Login");
dojo.require("UI.Highlight.Focus");
dojo.require("UI.Picker.Date");


(function() {
	var EVT_ERROR    = "error",
		EVT_SUCCESS  = "success",
		EVT_COMPLETE = "complete",
		EVT_SUBMIT   = "submit";
	var PATHS_NCHUB  = PPP.config.paths.nchub;


	dojo.declare("UI.Widgets.User.Login", UI.Base, {
		constructor: function() {
			this.container       = dojo.byId(dojo.byId("wdg-login"));
			this.links           = dojo.query("a", this.container);
			this.datePicker      = new UI.Picker.Date(dojo.byId("wdg-login-dob"));
			this.requireDob      = false;
			this.requirePassword = true;

			this.dom = {
				input: {
					username: dojo.byId("wdg-login-inp-username"),
					password: dojo.byId("wdg-login-inp-password"),
					dob:      this.datePicker.getNodes()
				},
				options: {
					username: dojo.byId("wdg-login-chk-remember-username"),
					password: dojo.byId("wdg-login-chk-remember-password")
				}
			};

			this.btnLogin = dojo.byId("wdg-login-btn-login");
			this.blocking = false;

			setBehaviour.apply(this);

			// If the form is blocked
			if ( dojo.hasClass(this.container, "fatal") ) {
				// Destroy login logic
				this.destroy();
				return null;
			}
		},

		/**
		 * Set page target for forgot username/password and for parent login
		 * @param string target The value for the `target` attribute of each link (optional; default: "_self").
		 */
		setAuxiliaryLinkTarget: function(target) {
			dojo.forEach(this.links, function(link) {
				link.target = target || "_self";
			});
			return this;
		},

		enableSubmit: function() {
			dojo.removeClass(this.btnLogin, "disabled");
			this.btnLogin.disabled = false;
			return this;
		},

		disableSubmit: function() {
			if ( !dojo.hasClass(this.btnLogin, "disabled") ) {
				dojo.addClass(this.btnLogin, "disabled");
			}

			this.btnLogin.disabled = true;
			return this;
		},

		unblock: function() {
			this.blocking = false;
			return this;
		},

		useToken: function() {
			this.requirePassword = false;
			return this;
		},

		usePassword: function() {
			this.requirePassword = true;
			return this;
		},

		submit: function() {
			try {
				// Make sure focus is kept on the form for users who like to press enter multiple times
				// and use excellent BIB browsers like IE
				this.dom.input.password.focus();
			} catch (e) {
				// try/catch because IE will flip if you try to focus on a non-visible element
			}

			if ( this.blocking ) {
				return this;
			}

			var values    = this.getValues(),
				startTime = null,
				request   = {
					useToken:        !this.requirePassword,
					username:        values.input.username,
					password:        this.requirePassword ? values.input.password : null,
					dob:             values.input.dob.month + "-" + values.input.dob.day + "-" + values.input.dob.year,
					persistUsername: +values.options.username,
					persistPassword: +values.options.password
				};

			if ( !values.input.username || (this.requirePassword && !values.input.password) ) {
				return this;
			}

			// Check to see if cookies are enabled
			if ( !UI.Util.Cookie.test() ) {
				this.blocking = true;

				UI.Widgets.Hud.getNoCookieDialog().open();

				setTimeout(dojo.hitch(this, function() {
					this.blocking = false;
				}), 1);

				return;
			}

			// Check to see if login is allowed
			if ( PPP.config.global.is_hub_closed ) {
				this.blocking = true;

				UI.Widgets.Hud.getMaintenanceDialog(PPP.config.global.msg_hub_closed).open();

				setTimeout(dojo.hitch(this, function() {
					this.blocking = false;
				}), 1);

				return;
			}

			this.triggerEvent(EVT_SUBMIT);

			this.blocking = true;
			this.disableSubmit();
			dojo.addClass(this.container, "pending");
			startTime = new Date();

			dojo.xhrPost({
				preventCache: true,
				handleAs:     "json",
				url:          "/services/auth",
				content:      request,
				handle: dojo.hitch(this, function(response, ioArgs) {
					isXhrActive = false;
					this._log("http", ioArgs.xhr.status);
				}),
				load: dojo.hitch(this, function(response) {
					var duration = new Date() - startTime;

					// Park is closed
					if ( response.closed ) {
						this._log("process/closed", duration);
						new UI.Widgets.User.Access.Blocked().open();
						return;
					}

					// User is banned
					if ( response.banned ) {
						this._log("process/banned", duration);
						dojo.removeClass(this.container, "pending");

						var dlgBanned = dojo.byId("wdg-login-dlg-banned");
						dojo.byId("layout-wrapper").appendChild(dlgBanned);

						dojo.connect(dojo.byId("wdg-login-btn-banned"), "click", function() {
							window.location = UI.Widgets.Hud.getHomePage();
						});
						new UI.Dialog.Generic(dlgBanned).useModal().open();
						return;
					}
					// Great success
					else if ( response.status ) {
						this._log("process/success", duration);

						// Update the global user object
						PPP.data.user.accountId    = response.accountId;
						PPP.data.user.petId        = response.pet.id;
						PPP.data.user.username     =
						PPP.data.user.displayName  = values.input.username;
						PPP.data.user.token        = response.token;
						PPP.data.user.isLoggedIn   = true;
						PPP.data.user.isFirstLogin = response.first;
						PPP.data.user.isActivated  = response.active;
						PPP.data.user.isChild      = response.coppa;
						PPP.data.user.isLegacy     = response.isLegacy;

						// Automagically login to the nchub as well
						UI.Widgets.User.Login.nchubLogin(values.input.username, null, response.token);

						var ncStartTime = new Date();

						// Wait for the nchub jsonp response to return and call this method.  Finish up the rest
						UI.Widgets.User.Login.nchubLoginResponse = (function(context, response) {
							var gatewayTotal      = 2,
								completedGateWays = 0;

							return function(forceCompletion) {
								context._log("nchub/process/" + (forceCompletion === true ? "timeout" : "responded"), new Date() - ncStartTime);

								if ( forceCompletion || ++completedGateWays == gatewayTotal ) {
									dojo.removeClass(context.container, "error");
									dojo.removeClass(context.container, "pending");
									dojo.addClass(context.container, "accept");

									context.triggerEvent(EVT_SUCCESS, response);
									context.triggerEvent(EVT_COMPLETE, response);

									UI.Widgets.User.Login.nchubLoginResponse = function() {};
								}
							};
						})(this, response);

						// Wait a total of 7 seconds for nchub to respond.  If it doesn't respond by then,
						// ditch the response so that the user can get on with using the site
						setTimeout(function() {
							UI.Widgets.User.Login.nchubLoginResponse(true);
						}, 7000);
					}
					// Great failure
					else {
						dojo.removeClass(this.container, "pending");
						dojo.addClass(this.container, "error");

						if ( response.invalidCredentials ) {
							this._log("process/invalid-credentials",       duration);
							this._log("process/invalid-credentials/count", response.failedAttempts);
						}

						if ( response.deny ) {
							this._log("process/frequency-ban", duration);
							// Lock user out of login form
							dojo.addClass(this.container, "fatal");
							this.destroy();
						}
						else if ( (this.requireDob = response.requireDob) ) {
							dojo.addClass(this.container, "critical");
						}

						// Automatically log the user out of the NC HUB on every login failure
						new Image().src = PATHS_NCHUB.logout.normal;
						new Image().src = PATHS_NCHUB.logout.secure;

						this.enableSubmit();
						this.triggerEvent(EVT_ERROR, response);
						this.triggerEvent(EVT_COMPLETE, response);
					}

					this.blocking = false;
				}),
				error: dojo.hitch(this, function() {
					dojo.removeClass(this.container, "pending");
					this.blocking = false;
					this.enableSubmit();
					this.triggerEvent(EVT_ERROR);
					this.triggerEvent(EVT_COMPLETE);
				})
			});

			return this;
		},

		logout: function(clearPreferences, callback) {
			// Auto logout of associated nchub
			new Image().src = PATHS_NCHUB.logout.normal;
			new Image().src = PATHS_NCHUB.logout.secure;

			this.unblock();

			// Log out of PPP
			dojo.xhrGet({
				preventCache: true,
				handleAs:     "json",
				url:          "/services/auth/logout",
				content: {
					clearPreferences: clearPreferences
				}
			}).addBoth(callback);
		},

		reset: function() {
			dojo.removeClass(this.container, "accept");
			dojo.removeClass(this.container, "error");
			dojo.removeClass(this.container, "pending");

			this.dom.input.username.value = "";
			this.dom.input.password.value = "";

			this.unblock();
		},

		getValues: function() {
			var values = {
				input: {
					username: this.dom.input.username.value,
					password: this.dom.input.password.value,
					dob:      {
						month: this.dom.input.dob.month.value,
						day:   this.dom.input.dob.day.value,
						year:  this.dom.input.dob.year.value
					}
				},
				options: {
					username: this.dom.options.username.checked,
					password: this.dom.options.password.checked
				}
			};


			return values;
		},

		setValues: function(values) {
			values = dojo.mixin(this.getValues(), values);

			this.dom.input.username.value     = values.input.username;
			this.dom.input.password.value     = values.input.password;
			this.dom.options.username.checked = !!values.options.username;
			this.dom.options.password.checked = !!values.options.password;
			return this;
		},

		isDobRequired: function() {
			return this.requireDob;
		},

		destroy: function() {
			UI.Widgets.User.Login.prototype.submit = function() {
				return false;
			};
		},

		_log: function(label, value) {
			UI.Util.Log.event("login/" + (this.requirePassword ? "password/" : "token/") + label, value);
		}
	});

	UI.Widgets.User.Login.nchubLogin = function(username, password, token, callbackName) {
		var script = document.createElement("script");

		script.src = PATHS_NCHUB.login.normal +
			"?username=" + escape(username) +
			"&password=" + (password ? escape(password) : "") +
			"&token="    + (token    ? escape(token)    : "") +
			"&callback=" + escape(callbackName || "UI.Widgets.User.Login.nchubLoginResponse");
		document.body.appendChild(script);

		script = document.createElement("script");
		script.src = PATHS_NCHUB.login.secure +
			"?username=" + escape(username) +
			"&password=" + (password ? escape(password) : "") +
			"&token="    + (token    ? escape(token)    : "") +
			"&callback=" + escape(callbackName || "UI.Widgets.User.Login.nchubLoginResponse");
		document.body.appendChild(script);
	};

	/**
	 * One time initialization stuff
	 */
	var setBehaviour = function() {
		var container = this.container,
			focusable = [
				this.dom.input.username,
				this.dom.input.password,
				this.dom.input.dob.month,
				this.dom.input.dob.day,
				this.dom.input.dob.year
			],
			inpUsername             = this.dom.input.username,
			inpPassword             = this.dom.input.password,
			chkRememberUsername     = this.dom.options.username,
			chkRememberPassword     = this.dom.options.password,
			autoCompletePoleTimerId = 0,
			self                    = this;

		/* Focus behavior */
		dojo.forEach(focusable, function(node) {
			new UI.Highlight.Focus(node);
		});

		/* Submit event */
		dojo.connect(dojo.query("form", container)[0], "submit", function(event) {
			event.stopPropagation();
			dojo.stopEvent(event);
			self.submit();
			return false;
		});
		dojo.connect(this.btnLogin, "click", function() {
			self.btnLogin.blur();
			self.submit();
		});

		var btnSubmitBehavior = dojo.hitch(this, function() {
			if ( inpUsername.value && (!this.requirePassword || inpPassword.value) ) {
				clearInterval(autoCompletePoleTimerId);
				self.enableSubmit();
			} else {
				self.disableSubmit();
			}
		});

		btnSubmitBehavior();

		/* Detect autocomplete */
		dojo.connect(inpUsername, "focus", function() {
			autoCompletePoleTimerId = setInterval(function() {
				btnSubmitBehavior();
			}, 100);
		});
		dojo.connect(inpUsername, "blur", function() {
			clearInterval(autoCompletePoleTimerId);
			btnSubmitBehavior();
		});

		/* Detect credentials input */
		dojo.map(['change', 'keyup'], function(event) {
			dojo.map([inpUsername, inpPassword], function(node) {
				dojo.connect(node, event, btnSubmitBehavior);
			});
		});

		/* Persistence options */
		dojo.map(['change', 'keyup'], function(event) {
			dojo.connect(chkRememberUsername, event, function() {
				if ( !chkRememberUsername.checked ) {
					chkRememberPassword.checked = false;
				}
			});
		});

		dojo.map(['change', 'keyup'], function(event) {
			dojo.connect(chkRememberPassword, event, function() {
				if ( chkRememberPassword.checked ) {
					chkRememberUsername.checked = true;
				}
			});
		});

		// Connect exit button
		dojo.connect(dojo.byId("wdg-login-btn-blocked-exit"), "click", function() {
			window.location = UI.Widgets.Hud.getHomePage();
		});
	};
})();
