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

import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;

import kieker.common.record.AbstractMonitoringRecord;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * This class is a singleton scoped {@code Spring} bean containing properties, constants, methods and even some localized texts which are used at various parts in
 * the application. While some of the properties are loaded directly from the resource bundles, some other are injected via {@code Spring}. This makes sure that most
 * of the constants could be modified without recompiling the whole application.<br>
 * </br>
 * 
 * If you want to change the values, take a look either at the files in the {@code resources} directory or at the {@code Spring} configuration files.<br>
 * </br>
 * 
 * The class implements the {@link Serializable} interface to make sure that some session scoped beans can use this class. It is furthermore singleton scoped as the
 * properties are the same for every user. Therefore only one instance of this class is necessary.<br>
 * </br>
 * 
 * As this bean is {@code Spring} managed, it is <b>not</b> recommended to use the constructor or the setter methods.
 * 
 * @author Nils Christian Ehmke
 */
@Component
@Scope("singleton")
public final class GlobalPropertiesBean implements Serializable {

	private static final long serialVersionUID = 4253541090603377504L;

	private static final String RESOURCE_BUNDLE_NAME_COMMON = "lang.Common";
	private static final String RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE = "lang.ProjectOverviewPage";
	private static final String RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE = "lang.AnalysisEditorPage";
	private static final String RESOURCE_BUNDLE_NAME_COCKPIT_EDITOR_PAGE = "lang.CockpitEditorPage";
	private static final String RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE = "lang.UserManagementPage";
	private static final String RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE = "lang.ControllerPage";

	private static final String PROPERTY_MSG_PROJECT_LOADING_EXCEPTION = "msgProjectLoadingException";
	private static final String PROPERTY_MSG_PROJECT_SAVED = "msgProjectSaved";
	private static final String PROPERTY_MSG_PLUGIN_CREATION_EXCEPTION = "msgPluginCreationException";
	private static final String PROPERTY_MSG_REPOSITORY_CREATION_EXCEPTION = "msgRepositoryCreationException";
	private static final String PROPERTY_MSG_LIBRARY_UPLOADED = "msgLibraryUploaded";
	private static final String PROPERTY_MSG_PROJECT_CREATED = "msgProjectCreated";
	private static final String PROPERTY_MSG_PROJECT_DELETED = "msgProjectDeleted";
	private static final String PROPERTY_MSG_PROJECT_UPLOADED = "msgProjectUploaded";
	private static final String PROPERTY_MSG_PROJECT_SAVING_EXCEPTION = "msgProjectSavingException";
	private static final String PROPERTY_MSG_PROJECT_NOT_EXISTING_EXCEPTION = "msgProjectNotExistingException";
	private static final String PROPERTY_MSG_PROJECT_MODIFIED = "msgProjectModified";
	private static final String PROPERTY_MSG_LIBRARY_EXISTING_EXCEPTION = "msgLibraryExistingException";
	private static final String PROPERTY_MSG_LIBRARY_UPLOADING_EXCEPTION = "msgLibraryUploadingException";
	private static final String PROPERTY_NAME_GLOBAL_CONFIGURATION_COMPONENT = "nameGlobalConfigurationComponent";
	private static final String PROPERTY_MSG_PROJECT_RENAMED = "msgProjectRenamed";
	private static final String PROPERTY_MSG_ENTER_VALID_NAME = "msgEnterValidName";
	private static final String PROPERTY_MSG_PROJECT_CREATION_EXCEPTION = "msgProjectCreationException";
	private static final String PROPERTY_MSG_PROJECT_ALREADY_EXISTING_EXCEPTION = "msgProjectAlreadyExistingException";
	private static final String PROPERTY_MSG_PROJECT_DELETE_EXCEPTION = "msgProjectDeleteException";
	private static final String PROPERTY_MSG_PROJECT_STILL_RUNNING_EXCEPTION = "msgProjectStillRunningException";
	private static final String PROPERTY_MSG_PROJECT_UPLOAD_EXCEPTION = "msgProjectUploadException";

	private static final String PROPERTY_LOG_MSG_ANALYSIS_ALREADY_STARTED = "logMsgAnalysisAlreadyStarted";
	private static final String PROPERTY_LOG_MSG_ERROR_OCCURED = "logMsgErrorOccured";
	private static final String PROPERTY_LOG_MSG_ANALYSIS_NOT_STARTED = "logMsgAnalysisNotStarted";
	private static final String PROPERTY_LOG_MSG_ANALYSIS_ALREADY_INSTANTIATED = "logMsgAnalysisAlreadyInstantiated";
	private static final String PROPERTY_LOG_MSG_ERROR_DURING_INITIALIZATION = "logMsgErrorDuringInitialization";
	private static final String PROPERTY_LOG_MSG_NOT_INSTANTIATED = "logMsgNotInstantiated";
	private static final String PROPERTY_LOG_MSG_STARTING_ANALYSIS = "logMsgStartingAnalysis";
	private static final String PROPERTY_LOG_MSG_STOPPING_ANALYSIS = "logMsgStoppingAnalysis";
	private static final String PROPERTY_LOG_MSG_INSTANTIATING_ANALYSIS = "logMsgInstantiatingAnalysis";
	private static final String PROPERTY_LOG_MSG_CLEANING = "logMsgCleaning";
	private static final String PROPERTY_LOG_MSG_EMERGENCY_SHUTDOWN = "logMsgEmergencyShutdown";

	private static final String PROPERTY_MSG_ADD_USER = "msgAddedUserToDatabase";
	private static final String PROPERTY_MSG_MODIFIED_USER = "msgModifiedUserInDatabase";
	private static final String PROPERTY_MSG_DELETED_USER = "msgDeleteduserFromDatabase";
	private static final String PROPERTY_MSG_MODIFY_USER_EXCEPTION = "msgModifyUserException";
	private static final String PROPERTY_MSG_ADD_USER_EXCEPTION = "msgAddUserException";
	private static final String PROPERTY_MSG_DELETE_USER_EXCEPTION = "msgDeleteUserException";
	private static final String PROPERTY_MSG_ACCESS_DATABASE_EXCEPTION = "msgAccessDatabaseException";
	private static final String PROPERTY_MSG_USER_ALREADY_EXISTING = "msgUserAlreadyExisting";

	private static final String PROPERTY_NAME_DEFAULT_USER_NAME = "defaultUserName";
	private static final String PROPERTY_NAME_DEFAULT_VIEW_NAME = "defaultViewName";
	private static final String PROPERTY_NAME_DEFAULT_VIEW_DESCRIPTION = "defaultViewDescription";
	private static final String PROPERTY_NAME_DEFAULT_PROJECT_NAME = "defaultProjectName";

	private String themeCookieName;
	private String facesContextThemeKey;
	private String defaultTheme;
	private String analysisEditorShowUnitializedComponentsCookieName;
	private String analysisEditorGridSizeCookieName;
	private String analysisEditorGridColorCookieName;
	private String analysisEditorDefaultGridSize;
	private String analysisEditorDefaultGridColor;

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

	public String getMsgAddUser() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_ADD_USER);
	}

	public String getMsgModifiedUser() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_MODIFIED_USER);
	}

	public String getMsgDeletedUser() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_DELETED_USER);
	}

	public String getMsgAddUserException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_ADD_USER_EXCEPTION);
	}

	public String getMsgDeleteUserException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_DELETE_USER_EXCEPTION);
	}

	public String getMsgAccessDatabaseException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_ACCESS_DATABASE_EXCEPTION);
	}

	public String getMsgModifyUserException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_MODIFY_USER_EXCEPTION);
	}

	public String getLogMsgAnalysisAlreadyStarted() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_ANALYSIS_ALREADY_STARTED);
	}

	public String getLogMsgErrorOccured() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_ERROR_OCCURED);
	}

	public String getLogMsgAnalysisNotStarted() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_ANALYSIS_NOT_STARTED);
	}

	public String getLogMsgAnalysisAlreadyInstantiated() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_ANALYSIS_ALREADY_INSTANTIATED);
	}

	public String getLogMsgErrorDuringInitialization() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_ERROR_DURING_INITIALIZATION);
	}

	public String getLogMsgNotInstantiated() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_NOT_INSTANTIATED);
	}

	public String getLogMsgStartingAnalysis() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_STARTING_ANALYSIS);
	}

	public String getLogMsgStoppingAnalysis() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_STOPPING_ANALYSIS);
	}

	public String getLogMsgEmergencyShutdown() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_EMERGENCY_SHUTDOWN);
	}

	public String getLogMsgInstantiatingAnalysis() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_INSTANTIATING_ANALYSIS);
	}

	public String getLogMsgCleaning() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_CONTROLLER_PAGE, PROPERTY_LOG_MSG_CLEANING);
	}

	public void setFacesContextThemeKey(final String facesContextThemeKey) {
		this.facesContextThemeKey = facesContextThemeKey;
	}

	public String getFacesContextThemeKey() {
		return this.facesContextThemeKey;
	}

	public void setDefaultTheme(final String defaultTheme) {
		this.defaultTheme = defaultTheme;
	}

	public String getDefaultTheme() {
		return this.defaultTheme;
	}

	public void setAnalysisEditorGridSizeCookieName(final String analysisEditorGridSizeCookieName) {
		this.analysisEditorGridSizeCookieName = analysisEditorGridSizeCookieName;
	}

	public String getAnalysisEditorGridSizeCookieName() {
		return this.analysisEditorGridSizeCookieName;
	}

	public void setAnalysisEditorGridColorCookieName(final String analysisEditorGridColorCookieName) {
		this.analysisEditorGridColorCookieName = analysisEditorGridColorCookieName;
	}

	public String getAnalysisEditorGridColorCookieName() {
		return this.analysisEditorGridColorCookieName;
	}

	public void setAnalysisEditorDefaultGridSize(final String analysisEditorDefaultGridSize) {
		this.analysisEditorDefaultGridSize = analysisEditorDefaultGridSize;
	}

	public String getAnalysisEditorDefaultGridSize() {
		return this.analysisEditorDefaultGridSize;
	}

	public void setAnalysisEditorDefaultGridColor(final String analysisEditorDefaultGridColor) {
		this.analysisEditorDefaultGridColor = analysisEditorDefaultGridColor;
	}

	public String getAnalysisEditorDefaultGridColor() {
		return this.analysisEditorDefaultGridColor;
	}

	public void setThemeCookieName(final String themeCookieName) {
		this.themeCookieName = themeCookieName;
	}

	public String getThemeCookieName() {
		return this.themeCookieName;
	}

	public void setAnalysisEditorShowUnitializedComponentsCookieName(final String unitializedComponentsCookieName) {
		this.analysisEditorShowUnitializedComponentsCookieName = unitializedComponentsCookieName;
	}

	public String getAnalysisEditorShowUnitializedComponentsCookieName() {
		return this.analysisEditorShowUnitializedComponentsCookieName;
	}

	public String getMsgProjectRenamed() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_RENAMED);
	}

	public String getMsgProjectLoadingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_PROJECT_LOADING_EXCEPTION);
	}

	public String getMsgProjectSaved() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_PROJECT_SAVED);
	}

	public String getMsgPluginCreationException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_MSG_PLUGIN_CREATION_EXCEPTION);
	}

	public String getMsgRepositoryCreationException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_MSG_REPOSITORY_CREATION_EXCEPTION);
	}

	public String getMsgLibraryUploaded() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_MSG_LIBRARY_UPLOADED);
	}

	public String getMsgProjectCreated() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_CREATED);
	}

	public String getMsgProjectSavingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_PROJECT_SAVING_EXCEPTION);
	}

	public String getMsgProjectNotExistingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_PROJECT_NOT_EXISTING_EXCEPTION);
	}

	public String getDefaultViewName() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COCKPIT_EDITOR_PAGE, PROPERTY_NAME_DEFAULT_VIEW_NAME);
	}

	public String getDefaultViewDescription() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COCKPIT_EDITOR_PAGE, PROPERTY_NAME_DEFAULT_VIEW_DESCRIPTION);
	}

	public String getDefaultProjectName() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_NAME_DEFAULT_PROJECT_NAME);
	}

	public String getDefaultUserName() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_NAME_DEFAULT_USER_NAME);
	}

	public String getMsgProjectModified() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_PROJECT_MODIFIED);
	}

	public String getMsgLibraryExistingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_MSG_LIBRARY_EXISTING_EXCEPTION);
	}

	public String getMsgLibraryUploadingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_MSG_LIBRARY_UPLOADING_EXCEPTION);
	}

	public String getMsgProjectDeleted() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_DELETED);
	}

	public String getMsgProjectUploaded() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_UPLOADED);
	}

	public String getMsgProjectCreationException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_CREATION_EXCEPTION);
	}

	public String getMsgProjectAlreadyExistingException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_ALREADY_EXISTING_EXCEPTION);
	}

	public String getMsgEnterValidName() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_COMMON, PROPERTY_MSG_ENTER_VALID_NAME);
	}

	public String getMsgProjectDeleteException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_DELETE_EXCEPTION);
	}

	public String getMsgProjectStillRunningException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_STILL_RUNNING_EXCEPTION);
	}

	public String getMsgProjectUploadException() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_PROJECT_OVERVIEW_PAGE, PROPERTY_MSG_PROJECT_UPLOAD_EXCEPTION);
	}

	public String getGlobalConfigurationComponentName() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_ANALYSIS_EDITOR_PAGE, PROPERTY_NAME_GLOBAL_CONFIGURATION_COMPONENT);
	}

	public String getMsgUserAlreadyExisting() {
		return GlobalPropertiesBean.getLocalizedString(RESOURCE_BUNDLE_NAME_USER_MANAGEMENT_PAGE, PROPERTY_MSG_USER_ALREADY_EXISTING);
	}

	private static String getLocalizedString(final String resourceBundleName, final String key) {
		// Get the correct resource bundle for the current language (taken from the current context) and search it for the given key
		final Locale currLocale = FacesContext.getCurrentInstance().getELContext().getLocale();
		return ResourceBundle.getBundle(resourceBundleName, currLocale).getString(key);
	}

	/**
	 * This method shows a message by using the message-component of PrimeFaces (within the current context).
	 * 
	 * @param severity
	 *            The severity of the message.
	 * @param msg
	 *            The message to show.
	 */
	public static void showMessage(final Severity severity, final String msg) {
		GlobalPropertiesBean.showMessage(severity, msg, null);
	}

	/**
	 * This method shows a message by using the message-component of PrimeFaces (within the current context).
	 * 
	 * @param severity
	 *            The severity of the message.
	 * @param msg
	 *            The message to show.
	 * @param clientID
	 *            The client ID. This value can be used to show a message for a specific component.
	 */
	public static void showMessage(final Severity severity, final String msg, final String clientID) {
		FacesContext.getCurrentInstance().addMessage(clientID, new FacesMessage(severity, "", msg));
	}

	/**
	 * This method verifies whether the given object is an instance of {@link java.lang.String} or not. This is necessary due to the fact that the
	 * <i>instanceof</i>-command has not yet been implemented in JSF.
	 * 
	 * @param object
	 *            The object to be verified.
	 * 
	 * @return true if and only if the given object is an instance of String.
	 */
	public boolean checkString(final Object object) {
		return object instanceof String;
	}

	/**
	 * Shortens a given name. If the name is longer than the given number of characters, only the first maxChar - 3 characters of the string will be used and the
	 * last three will be replaced with dots.
	 * 
	 * @param name
	 *            The name to be shortened.
	 * @param maxChar
	 *            The maximal number of characters.
	 * 
	 * @return The shortened name.
	 */
	public String shortenLongName(final String name, final int maxChar) {
		if (name.length() > maxChar) {
			return name.substring(0, maxChar - 3) + "...";
		} else {
			return name;
		}
	}

	/**
	 * This method reads parameters from the request parameter map of the current context. The parameters are converted to actual objects, based on the given types.
	 * It is assumed (but not checked) that both arrays have the same size.
	 * 
	 * @param parameterNames
	 *            The name of the parameters within the request parameter map.
	 * @param types
	 *            The actual types of the parameters. The method will try to convert the strings to those types.
	 * 
	 * @return An array containing the actual parameter objects.
	 */
	public static Object[] convertObjectsFromParameterMap(final String[] parameterNames, final Class<?>[] types) {
		final int numberOfObjects = parameterNames.length;
		final Map<String, String> parameterMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
		final String[] parameters = new String[numberOfObjects];

		for (int i = 0; i < numberOfObjects; i++) {
			parameters[i] = parameterMap.get(parameterNames[i]);

		}

		// We can borrow a method from the Kieker framework to convert the strings
		return AbstractMonitoringRecord.fromStringArrayToTypedArray(parameters, types);
	}

}
