class Authentication {

	/**
	 * Will initialize the authentication manager.
	 */
	static initialize() {
		Authentication.allPermissions = [ "accounts", "user_role", "user_role_grant", "user", "user_samerole", "user_all", "report_user", "card", "card_category", "project", "project_all", "project_assigned", "report_project", "period", "period_all", "multiuser", "project_user", "project_user_tariff" ];

		TimeCards.dataManager.addDataListener("user", {
			is_session_user: true
		}, Authentication.onSessionUserUpdate);
	}
	
	/**
	 * Will return the permissions of the logged in user or an empty object if the permissions were not yet loaded.
	 * @return The permissions object of the logged in user.
	 */
	static getPermissions() {
		if (!Authentication.permissions) {
			console.warn("Trying to get the permissions of the logged in user before they could be loaded.");
			return { };
		}

		return Authentication.permissions;
	}

	/**
	 * Checks whether the logged in user has the required access ("w" or "r") on the given resource or not and returns this information.
	 * @return true if the resource is available to the user, false if not.
	 */
	static isResourceAvailable(permission, type) {
		var permissions = this.getPermissions();

		if (permission in permissions && (permissions[permission] == type || (type == "r" && permissions[permission] == "w"))) {
			return true;
		}

		return false;
	}

	/**
	 * Shows or hides the elements on the page depending on whether the user has access to them or not.
	 * This is done via CSS classes.
	 */
	static applyPermissionsToInterface() {
		//Do nothing if there is no logged in user.
		if (!Authentication.currentUser) {
			console.warn("Trying to apply the permissions to the interface, but there is no current user.");
			return;
		}

		

		var permissions = Authentication.getPermissions();

		//Re-enable all elements that were be disabled because of the lack of permission in the last update.
		if (Authentication.elementsDisabledBecauseOfLackingPermissions) {
			for (var i = 0; i < Authentication.elementsDisabledBecauseOfLackingPermissions.length; i++) {
				if (Authentication.elementsDisabledBecauseOfLackingPermissions[i].hasAttribute("disabled")) {
					Authentication.elementsDisabledBecauseOfLackingPermissions[i].removeAttribute("disabled");
				}
			}
		}

		//Disable all elements that this user has no access to.
		Authentication.elementsDisabledBecauseOfLackingPermissions = [ ];
		for (var i = 0; i < Authentication.allPermissions.length; i++) {
			var permission = Authentication.allPermissions[i];

			var readElements = document.body.querySelectorAll(".available__" + permission);
			var writeElements = document.body.querySelectorAll(".available_w__" + permission);

			if (!permission in permissions) {
				for (var j = 0; j < readElements.length; j++) {
					Authentication.elementsDisabledBecauseOfLackingPermissions.push(readElements[j]);
				}
			}
			if ((!permission in permissions) || permissions[permission] == "r") {
				for (var j = 0; j < writeElements.length; j++) {
					Authentication.elementsDisabledBecauseOfLackingPermissions.push(writeElements[j]);
				}
			}
		}

		for (var i = 0; i < Authentication.elementsDisabledBecauseOfLackingPermissions.length; i++) {
			Authentication.elementsDisabledBecauseOfLackingPermissions[i].setAttribute("disabled", "");
		}

		//Remove the old permission classes from the body.
		for (var i = 0; i < Authentication.allPermissions.length; i++) {
			var permission = Authentication.allPermissions[i];
			document.body.classList.remove("enable__" + permission);
			document.body.classList.remove("enable_w__" + permission);
		}

		//Add the new permission classes.
		for (var permission in permissions) {
			document.body.classList.add("enable__" + permission);

			if (permissions[permission] == "w") {
				document.body.classList.add("enable_w__" + permission);
			}
		}
	}

	/**
	 * Updates the given element based on the permissions.
	 */
	static applyPermissionsToElement(element) {
		if (!Authentication.elementsDisabledBecauseOfLackingPermissions) {
			Authentication.elementsDisabledBecauseOfLackingPermissions = [ ];
		}

		var index = Authentication.elementsDisabledBecauseOfLackingPermissions.indexOf(element);
		if (index != -1) {
			Authentication.elementsDisabledBecauseOfLackingPermissions[index].removeAttribute("disabled");
			Authentication.elementsDisabledBecauseOfLackingPermissions.splice(index, 1);
		}

		var permissions = Authentication.getPermissions();

		for (var i = 0; i < Authentication.allPermissions.length; i++) {
			var permission = Authentication.allPermissions[i];

			var readElements = document.body.querySelectorAll(".available__" + permission);
			var writeElements = document.body.querySelectorAll(".available_w__" + permission);

			if ((!(permission in permissions) && (element.classList.contains("available__" + permission) || element.classList.contains("available_w__" + permission))) || ((permission in permissions) && permissions[permission] == "r" && element.classList.contains("available_w__" + permission))) {
				Authentication.elementsDisabledBecauseOfLackingPermissions.push(element);
				element.setAttribute("disabled", "");
			}
		}
	}

	/**
	 * Called when the session user was loaded or updated. A deletion is unlikely, but not impossible.
	 */
	static onSessionUserUpdate(userId, user) {
		if (!user) {
			alert("Your user was deleted.");

			//Perform the logout action when the session user was deleted.
			Authentication.logOut();

			return;
		}

		//Create the data listener for the current user's role if the user has been loaded for the first time.
		if (!Authentication.currentUser || Authentication.currentUser.user_id != user.user_id) {
			//Remove the existing data listener if it is a change in user.
			if (Authentication.currentUser && Authentication.currentUser.user_id != user.user_id) {
				TimeCards.dataManager.removeDataListener("user_role", Authentication.onSessionUserRoleUpdate);
			}

			TimeCards.dataManager.addDataListener("user_role", {
				role_id: user.id_role
			}, Authentication.onSessionUserRoleUpdate);
		}

		//Apply the current user information.
		Authentication.currentUser = user;

		//Get the permissions of the current user's role.
		//TODO: Enable this when the roles are using the data manager.
		//Authentication.permissions = TimeCards.dataManager.getEntity("user_role", user.id_role).permissions;
	}

	/**
	 * Called when the session user's role was loaded or updated. A deletion is unlikely, but not impossible.
	 */
	static onSessionUserRoleUpdate(roleId, role) {
		if (!role) {
			alert("The role of your user was deleted.");

			//Perform the logout action when the session user's role was deleted.
			Authentication.logOut();

			return;
		}

		Authentication.permissions = role.permissions;
	}

	/**
	 * Invalidates the session on the server.
	 * After this action is done, the local session value is deleted, too.
	 * Finally, the app will reload and therefore show the login screen.
	 */
	static logOut() {
		TimeCards.dataManager.clearQueue(true);
		if (Authentication.currentUser) {
			TimeCards.dataManager.store("user", Authentication.currentUser.user_id, {
				session: null
			}, Authentication.onLogoutStored);
		}
		else {
			//Reload immediately if the user was never properly logged in during this runtime.
			Authentication.onLogoutStored();
		}
	}

	/**
	 * Called when the logout action has returned from the server.
	 */
	static onLogoutStored(userId, user) {
		document.cookie = "session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
		window.location.reload();
	}

}