/***************************************************************************
 * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package kieker.webgui.web.beans.session;

import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import kieker.webgui.web.beans.application.GlobalPropertiesBean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

/**
 * This bean contains information about the user of this session (like the properties and configurations). This class is a {@code Spring} managed bean with session
 * scope. This means also that it is possible to login the same user multiple times.<br>
 * </br>
 * 
 * As it is a session bean, it must implement the {@link Serializable} interface.
 * 
 * @author Nils Christian Ehmke
 */
@Component
@Scope("session")
public final class UserBean implements Serializable {

	private static final long serialVersionUID = 6422563876003638348L;

	private boolean showUnitializedComponents;
	private String lookAndFeel;
	private String gridColor;
	private int gridSize;

	@Autowired
	private GlobalPropertiesBean globalPropertiesBean;

	/**
	 * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b>
	 */
	public UserBean() {
		// No code necessary
	}

	/**
	 * This method initializes the bean. In fact it loads the default values before trying to load the values of the user from their cookies. <b>Do not call this
	 * method. The method is Spring managed.</b>
	 */
	@PostConstruct
	protected void initialize() { // NOPMD (has to be protected)
		this.loadDefaultValues();
		this.loadValuesFromCookies();
	}

	/**
	 * Returns the name of the user of the current session. If something goes wrong during the search, it returns a human readable 'N/A'.
	 * 
	 * @return The user name of the session user.
	 */
	public String getUsername() {
		String username = "N/A";

		final UserDetails userDetails = UserBean.getUserDetails();
		if (userDetails != null) {
			username = userDetails.getUsername();
		}

		return username;
	}

	/**
	 * Delivers the role of the current user. If something goes wrong during the search, it returns a human readable 'N/A'.
	 * 
	 * @return The current userrole.
	 */
	public String getUserrole() {
		String userrole = "N/A";

		final UserDetails userDetails = UserBean.getUserDetails();
		if (userDetails != null) {
			final Iterator<? extends GrantedAuthority> authorities = userDetails.getAuthorities().iterator();
			// If there is at least one authority, we know that it must be the first (we only have one authority per user).
			if (authorities.hasNext()) {
				userrole = authorities.next().getAuthority();
			}
		}

		return userrole;
	}

	private static UserDetails getUserDetails() {
		final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		if (principal instanceof UserDetails) {
			return (UserDetails) principal;
		}
		return null;
	}

	/**
	 * The setter for the property {@link UserBean#globalPropertiesBean}. <b>Do not use this method. This property is Spring managed.</b>
	 * 
	 * @param globalPropertiesBean
	 *            The new value for the property.
	 */
	public synchronized void setGlobalPropertiesBean(final GlobalPropertiesBean globalPropertiesBean) {
		this.globalPropertiesBean = globalPropertiesBean;
	}

	public synchronized String getLookAndFeel() {
		return this.lookAndFeel;
	}

	/**
	 * The setter for the property {@link UserBean#lookAndFeel}. The method tries to save the value in the cookies of the user.
	 * 
	 * @param lookAndFeel
	 *            The new value for the property.
	 */
	public synchronized void setLookAndFeel(final String lookAndFeel) {
		this.lookAndFeel = lookAndFeel;
		UserBean.saveValueInCookie(this.globalPropertiesBean.getThemeCookieName(), lookAndFeel);
	}

	public synchronized String getGridColor() {
		return this.gridColor;
	}

	public synchronized boolean isShowUnitializedComponents() {
		return this.showUnitializedComponents;
	}

	public synchronized void setShowUnitializedComponents(final boolean showUnitializedComponents) {
		this.showUnitializedComponents = showUnitializedComponents;
		UserBean.saveValueInCookie(this.globalPropertiesBean.getAnalysisEditorShowUnitializedComponentsCookieName(), Boolean.toString(showUnitializedComponents));
	}

	/**
	 * The setter for the property {@link UserBean#gridColor}. The method tries to save the value in the cookies of the user.
	 * 
	 * @param gridColor
	 *            The new value for the property.
	 */
	public synchronized void setGridColor(final String gridColor) {
		this.gridColor = gridColor;
		UserBean.saveValueInCookie(this.globalPropertiesBean.getAnalysisEditorGridColorCookieName(), gridColor);
	}

	public synchronized int getGridSize() {
		return this.gridSize;
	}

	/**
	 * The setter for the property {@link UserBean#gridSize}. The method tries to save the value in the cookies of the user.
	 * 
	 * @param gridSize
	 *            The new value for the property.
	 */
	public synchronized void setGridSize(final int gridSize) {
		this.gridSize = gridSize;
		UserBean.saveValueInCookie(this.globalPropertiesBean.getAnalysisEditorGridSizeCookieName(), Integer.toString(gridSize));
	}

	/**
	 * This method tries to load the default values of the properties from the {@link GlobalPropertiesBean} and the faces context.
	 */
	private synchronized void loadDefaultValues() {
		// Get the parameters within the current context.
		final Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); // NOPMD (No concurrent access)
		// Try to find the default theme within the parameters.
		if (params.containsKey(this.globalPropertiesBean.getFacesContextThemeKey())) {
			this.lookAndFeel = params.get(this.globalPropertiesBean.getFacesContextThemeKey());
		} else {
			// Use the default theme.
			this.lookAndFeel = this.globalPropertiesBean.getDefaultTheme();
		}

		this.gridColor = this.globalPropertiesBean.getAnalysisEditorDefaultGridColor();
		this.gridSize = Integer.parseInt(this.globalPropertiesBean.getAnalysisEditorDefaultGridSize());
	}

	/**
	 * This method tries to load the values of the properties from the user's cookies.
	 */
	private synchronized void loadValuesFromCookies() {
		// Get the available cookies
		final Map<String, Object> cookies = FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap(); // NOPMD (No concurrent access)

		// Now load the values if available
		if (cookies.containsKey(this.globalPropertiesBean.getThemeCookieName())) {
			this.lookAndFeel = ((Cookie) cookies.get(this.globalPropertiesBean.getThemeCookieName())).getValue();
		}
		if (cookies.containsKey(this.globalPropertiesBean.getAnalysisEditorGridColorCookieName())) {
			this.gridColor = ((Cookie) cookies.get(this.globalPropertiesBean.getAnalysisEditorGridColorCookieName())).getValue();
		}
		if (cookies.containsKey(this.globalPropertiesBean.getAnalysisEditorGridSizeCookieName())) {
			this.gridSize = Integer.parseInt(((Cookie) cookies.get(this.globalPropertiesBean.getAnalysisEditorGridSizeCookieName())).getValue());
		}
		if (cookies.containsKey(this.globalPropertiesBean.getAnalysisEditorShowUnitializedComponentsCookieName())) {
			this.showUnitializedComponents = Boolean.parseBoolean(((Cookie) cookies.get(this.globalPropertiesBean
					.getAnalysisEditorShowUnitializedComponentsCookieName())).getValue());
		}
	}

	/**
	 * This method saves the given values in a cookie with the given name, using the maximal available age for the cookie.
	 * 
	 * @param cookieName
	 *            The name of the cookie.
	 * @param cookieValue
	 *            The value for the cookie.
	 */
	private static void saveValueInCookie(final String cookieName, final String cookieValue) {
		// Create the cookie and make sure that it will be saved for a year (maximum age)
		final Cookie cookie = new Cookie(cookieName, cookieValue);
		cookie.setMaxAge(60 * 60 * 24 * 365);

		// Deliver the cookie
		final HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
		response.addCookie(cookie);
	}
}
