/***************************************************************************
 * 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.view;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;

import kieker.common.logging.Log;
import kieker.common.logging.LogFactory;
import kieker.webgui.common.exception.DataAccessException;
import kieker.webgui.common.exception.UserAlreadyExistingException;
import kieker.webgui.domain.User;
import kieker.webgui.service.IUserService;
import kieker.webgui.web.beans.application.GlobalPropertiesBean;
import kieker.webgui.web.beans.request.user.NewUserBean;

import org.primefaces.context.RequestContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * This class contains the necessary data behind an instance of the user management page. It is a Spring managed bean with view scope to make sure that one user
 * (even in one session) can open multiple instances of the page at a time without causing any problems.
 * 
 * @author Nils Christian Ehmke
 */
@Component
@Scope("view")
public final class UserManagementBean {

	private static final Log LOG = LogFactory.getLog(UserManagementBean.class);

	private List<User> availableUsers = new ArrayList<User>();
	private User selectedUserCopy;
	private User selectedUser;

	@Autowired
	private GlobalPropertiesBean globalPropertiesBean;
	@Autowired
	private IUserService userService;

	/**
	 * Default constructor. <b>Do not call this constructor manually. It will only be accessed by Spring.</b>
	 */
	public UserManagementBean() {
		// No code necessary
	}

	/**
	 * This method initializes the bean. <b>Do not call this method manually. It will only be accessed by Spring.</b>
	 */
	@PostConstruct
	protected void initialialize() { // NOPMD (has to be protected)
		this.updateList();
	}

	/**
	 * This method adds the given user to the database and informs about success via the growl component.
	 * 
	 * @param newUserBean
	 *            The bean containing the necessary data to create the user.
	 */
	public void addUser(final NewUserBean newUserBean) {
		final User user = new User(newUserBean.getUsername(), newUserBean.getPassword(), newUserBean.getRole(), newUserBean.isActive());
		try {
			// Add the user to the database and - if that does not fail - to our list.
			this.userService.addUser(user);
			this.availableUsers.add(user);

			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgAddUser());
		} catch (final UserAlreadyExistingException ex) {
			LOG.error("Could not add the user to the database", ex);
			// If the user exists already, we use a specific callback. We provide the user the possibility to correct the problem without having to fill the form
			// again.
			RequestContext.getCurrentInstance().addCallbackParam("fail", true);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgUserAlreadyExisting(), newUserBean.getMessageTarget()
					.getClientId());
		} catch (final DataAccessException ex) {
			LOG.error("Could not add the user to the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgAddUserException());
		}
	}

	/**
	 * This method removes the currently selected user from the database and informs about success via the growl component.
	 */
	public void deleteUser() {
		try {
			this.userService.deleteUser(this.selectedUser);
			this.availableUsers.remove(this.selectedUser);

			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgDeletedUser());
		} catch (final DataAccessException ex) {
			LOG.error("Could not delete the user from the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgDeleteUserException());
		}
	}

	/**
	 * This method edits the currently selected user within the database and informs about success via the growl component. The password will not be changed.
	 */
	public void editUser() {
		try {
			// Edit the user and replace the entry within our list
			this.userService.editUserWithoutPassword(this.selectedUserCopy);
			this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy);

			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgModifiedUser());
		} catch (final DataAccessException ex) {
			LOG.error("Could not modify the user within the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgModifyUserException());
		}
	}

	/**
	 * This method edits the currently selected user within the database and informs about success via the growl component. The password will be changed.
	 */
	public void editUserWithPassword() {
		try {
			// Edit the user and replace the entry within our list
			this.userService.editUserWithPassword(this.selectedUserCopy);
			this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy);

			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgModifiedUser());
		} catch (final DataAccessException ex) {
			LOG.error("Could not modify the user within the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgModifyUserException());
		}
	}

	/**
	 * This method changes the enabled state of the given user and informs about success via the growl component.
	 * 
	 * @param user
	 *            The user whose state should be changed.
	 */
	public void changeUserEnableState(final User user) {
		try {
			this.setSelectedUser(user);
			this.selectedUserCopy.setEnabled(!this.selectedUserCopy.isEnabled());
			this.userService.editUserWithoutPassword(this.selectedUserCopy);
			this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy);

			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgModifiedUser());
		} catch (final DataAccessException ex) {
			LOG.error("Could not modify the user within the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgModifyUserException());
		}
	}

	/**
	 * Updates the list containing the available users.
	 */
	public void updateList() {
		try {
			this.availableUsers = this.userService.getUsers();
		} catch (final DataAccessException ex) {
			LOG.error("An error occured while accessing the database", ex);
			GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgAccessDatabaseException());
		}
	}

	/**
	 * Setter for the property {@link UserManagementBean#selectedUser}.
	 * 
	 * @param selectedUser
	 *            The new value of the property.
	 */
	public void setSelectedUser(final User selectedUser) {
		// We remember the selected user, but we make also a copy. This is necessary, because otherwise we would have to do something like a rollback if, for
		// example, an edit within the DB fails.
		this.selectedUser = selectedUser;
		this.selectedUserCopy = selectedUser.copy();
	}

	public List<User> getUsers() {
		return this.availableUsers;
	}

	public User getSelectedUser() {
		return this.selectedUser;
	}

	public User getSelectedUserCopy() {
		return this.selectedUserCopy;
	}

}
