/**
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
 *
 * 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 org.jivesoftware.openfire.plugin.rest;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.ws.rs.core.Response;

import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.plugin.rest.entity.SystemProperties;
import org.jivesoftware.openfire.plugin.rest.entity.SystemProperty;
import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType;
import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.StringUtils;

import org.jivesoftware.openfire.plugin.rest.service.JerseyWrapper;

/**
 * The Class RESTServicePlugin.
 */
public class RESTServicePlugin implements Plugin, PropertyEventListener {
	
	/** The Constant INSTANCE. */
	public static final RESTServicePlugin INSTANCE = new RESTServicePlugin();

	private static final String CUSTOM_AUTH_FILTER_PROPERTY_NAME = "plugin.restapi.customAuthFilter";
	
	/** The secret. */
	private String secret;
	
	/** The allowed i ps. */
	private Collection<String> allowedIPs;
	
	/** The enabled. */
	private boolean enabled;
	
	/** The http auth. */
	private String httpAuth;
	
	/** The custom authentication filter */
	private String customAuthFilterClassName;


	/**
	 * Gets the single instance of RESTServicePlugin.
	 *
	 * @return single instance of RESTServicePlugin
	 */
	public static RESTServicePlugin getInstance() {
		return INSTANCE;
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager, java.io.File)
	 */
	public void initializePlugin(PluginManager manager, File pluginDirectory) {
		secret = JiveGlobals.getProperty("plugin.restapi.secret", "");
		// If no secret key has been assigned, assign a random one.
		if ("".equals(secret)) {
			secret = StringUtils.randomString(16);
			setSecret(secret);
		}
		
		// See if Custom authentication filter has been defined
		customAuthFilterClassName = JiveGlobals.getProperty("plugin.restapi.customAuthFilter", "");
		
		// See if the service is enabled or not.
		enabled = JiveGlobals.getBooleanProperty("plugin.restapi.enabled", false);

		// See if the HTTP Basic Auth is enabled or not.
		httpAuth = JiveGlobals.getProperty("plugin.restapi.httpAuth", "basic");

		// Get the list of IP addresses that can use this service. An empty list
		// means that this filter is disabled.
		allowedIPs = StringUtils.stringToCollection(JiveGlobals.getProperty("plugin.restapi.allowedIPs", ""));

		// Listen to system property events
		PropertyEventDispatcher.addListener(this);
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
	 */
	public void destroyPlugin() {
		// Stop listening to system property events
		PropertyEventDispatcher.removeListener(this);
	}

	/**
	 * Gets the system properties.
	 *
	 * @return the system properties
	 */
	public SystemProperties getSystemProperties() {
		SystemProperties systemProperties = new SystemProperties();
		List<SystemProperty> propertiesList = new ArrayList<SystemProperty>();
		
		for(String propertyKey : JiveGlobals.getPropertyNames()) {
			String propertyValue = JiveGlobals.getProperty(propertyKey);
			propertiesList.add(new SystemProperty(propertyKey, propertyValue));
		}
		systemProperties.setProperties(propertiesList);
		return systemProperties;

	}

	/**
	 * Gets the system property.
	 *
	 * @param propertyKey the property key
	 * @return the system property
	 * @throws ServiceException the service exception
	 */
	public SystemProperty getSystemProperty(String propertyKey) throws ServiceException {
		String propertyValue = JiveGlobals.getProperty(propertyKey);
		if(propertyValue != null) {
		return new SystemProperty(propertyKey, propertyValue);
		} else {
			throw new ServiceException("Could not find property", propertyKey, ExceptionType.PROPERTY_NOT_FOUND,
					Response.Status.NOT_FOUND);
		}
	}
	
	/**
	 * Creates the system property.
	 *
	 * @param systemProperty the system property
	 */
	public void createSystemProperty(SystemProperty systemProperty) {
		JiveGlobals.setProperty(systemProperty.getKey(), systemProperty.getValue());
	}
	
	/**
	 * Delete system property.
	 *
	 * @param propertyKey the property key
	 * @throws ServiceException the service exception
	 */
	public void deleteSystemProperty(String propertyKey) throws ServiceException {
		if(JiveGlobals.getProperty(propertyKey) != null) {
			JiveGlobals.deleteProperty(propertyKey);
		} else {
			throw new ServiceException("Could not find property", propertyKey, ExceptionType.PROPERTY_NOT_FOUND,
					Response.Status.NOT_FOUND);
		}
	}
	
	/**
	 * Update system property.
	 *
	 * @param propertyKey the property key
	 * @param systemProperty the system property
	 * @throws ServiceException the service exception
	 */
	public void updateSystemProperty(String propertyKey, SystemProperty systemProperty) throws ServiceException {
		if(JiveGlobals.getProperty(propertyKey) != null) {
			if(systemProperty.getKey().equals(propertyKey)) {
				JiveGlobals.setProperty(propertyKey, systemProperty.getValue());
			} else {
				throw new ServiceException("Path property name and entity property name doesn't match", propertyKey, ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION,
						Response.Status.BAD_REQUEST);
			}
		} else {
			throw new ServiceException("Could not find property for update", systemProperty.getKey(), ExceptionType.PROPERTY_NOT_FOUND,
					Response.Status.NOT_FOUND);
		}
	}

	
	/**
	 * Returns the loading status message.
	 *
	 * @return the loading status message.
	 */
	public String getLoadingStatusMessage() {
		return JerseyWrapper.getLoadingStatusMessage();
	}
	
	/**
	 * Reloads the Jersey wrapper.
	 */
	public String loadAuthenticationFilter(String customAuthFilterClassName) {
		return JerseyWrapper.tryLoadingAuthenticationFilter(customAuthFilterClassName);
	}
	
	/**
	 * Returns the secret key that only valid requests should know.
	 *
	 * @return the secret key.
	 */
	public String getSecret() {
		return secret;
	}

	/**
	 * Sets the secret key that grants permission to use the userservice.
	 *
	 * @param secret
	 *            the secret key.
	 */
	public void setSecret(String secret) {
		JiveGlobals.setProperty("plugin.restapi.secret", secret);
		this.secret = secret;
	}

	/**
	 * Returns the custom authentication filter class name used in place of the basic ones to grant permission to use the Rest services.
	 *
	 * @return custom authentication filter class name .
	 */
	public String getCustomAuthFilterClassName() {
		return customAuthFilterClassName;
	}

	/**
	 * Sets the customAuthFIlterClassName used to grant permission to use the Rest services.
	 *
	 * @param customAuthFilterClassName
	 *            custom authentication filter class name.
	 */
	public void setCustomAuthFiIterClassName(String customAuthFilterClassName) {
		JiveGlobals.setProperty(CUSTOM_AUTH_FILTER_PROPERTY_NAME, customAuthFilterClassName);
		this.customAuthFilterClassName = customAuthFilterClassName;
	}
	
	/**
	 * Gets the allowed i ps.
	 *
	 * @return the allowed i ps
	 */
	public Collection<String> getAllowedIPs() {
		return allowedIPs;
	}

	/**
	 * Sets the allowed i ps.
	 *
	 * @param allowedIPs the new allowed i ps
	 */
	public void setAllowedIPs(Collection<String> allowedIPs) {
		JiveGlobals.setProperty("plugin.restapi.allowedIPs", StringUtils.collectionToString(allowedIPs));
		this.allowedIPs = allowedIPs;
	}

	/**
	 * Returns true if the user service is enabled. If not enabled, it will not
	 * accept requests to create new accounts.
	 *
	 * @return true if the user service is enabled.
	 */
	public boolean isEnabled() {
		return enabled;
	}

	/**
	 * Enables or disables the user service. If not enabled, it will not accept
	 * requests to create new accounts.
	 *
	 * @param enabled
	 *            true if the user service should be enabled.
	 */
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
		JiveGlobals.setProperty("plugin.restapi.enabled", enabled ? "true" : "false");
	}

	/**
	 * Gets the http authentication mechanism.
	 *
	 * @return the http authentication mechanism
	 */
	public String getHttpAuth() {
		return httpAuth;
	}

	/**
	 * Sets the http auth.
	 *
	 * @param httpAuth the new http auth
	 */
	public void setHttpAuth(String httpAuth) {
		this.httpAuth = httpAuth;
		JiveGlobals.setProperty("plugin.restapi.httpAuth", httpAuth);
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.PropertyEventListener#propertySet(java.lang.String, java.util.Map)
	 */
	public void propertySet(String property, Map<String, Object> params) {
		if (property.equals("plugin.restapi.secret")) {
			this.secret = (String) params.get("value");
		} else if (property.equals("plugin.restapi.enabled")) {
			this.enabled = Boolean.parseBoolean((String) params.get("value"));
		} else if (property.equals("plugin.restapi.allowedIPs")) {
			this.allowedIPs = StringUtils.stringToCollection((String) params.get("value"));
		} else if (property.equals("plugin.restapi.httpAuth")) {
			this.httpAuth = (String) params.get("value");
		} else if(property.equals(CUSTOM_AUTH_FILTER_PROPERTY_NAME)) {
			this.customAuthFilterClassName = (String) params.get("value");
		}
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String, java.util.Map)
	 */
	public void propertyDeleted(String property, Map<String, Object> params) {
		if (property.equals("plugin.restapi.secret")) {
			this.secret = "";
		} else if (property.equals("plugin.restapi.enabled")) {
			this.enabled = false;
		} else if (property.equals("plugin.restapi.allowedIPs")) {
			this.allowedIPs = Collections.emptyList();
		} else if (property.equals("plugin.restapi.httpAuth")) {
			this.httpAuth = "basic";
		} else if(property.equals(CUSTOM_AUTH_FILTER_PROPERTY_NAME)) {
			this.customAuthFilterClassName = null;
		}
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.PropertyEventListener#xmlPropertySet(java.lang.String, java.util.Map)
	 */
	public void xmlPropertySet(String property, Map<String, Object> params) {
		// Do nothing
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.PropertyEventListener#xmlPropertyDeleted(java.lang.String, java.util.Map)
	 */
	public void xmlPropertyDeleted(String property, Map<String, Object> params) {
		// Do nothing
	}
}
