/* To change this template, choose Tools | Templates
 * and open the template in the editor. */
package lu.tudor.santec.gecamed.esante.gui.data;

import java.net.ConnectException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.ESanteProperty;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.ESanteConfigManagerBean;
import lu.tudor.santec.gecamed.esante.gui.webservice.SOAPUtilities;
import lu.tudor.santec.gecamed.esante.gui.webservice.Security;
import lu.tudor.santec.gecamed.esante.gui.webservice.WebserviceConstants;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Contains parameters that are necessary for establishing a connection to the different services of the eSanté platform
 * 
 * @author jens ferring, ortwin donak
 * 
 *         Based on infomed client sources.
 */
public class Configuration {

	/**
	 * Indicates that the login method has not yet been set
	 */
	public final static int LOGIN_NOT_SET = -1;
	/**
	 * Indicates that the user wants to login via username and password
	 */
	public final static int LOGIN_USERNAME_PASSWORD = 0;
	/**
	 * Indicates that the user wants to login via a smartcard or usb stick
	 */
	public final static int LOGIN_SMARTCARD = 1;
	/**
	 * Indicates that the user wants to login via the signing server (by using a token)
	 */
	public final static int LOGIN_SIGNING_SERVER = 2;

	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(Configuration.class.getName());

	/** Cache for user specific parameters (e.g. OIDs, service urls) */
	private HashMap<String, String> propertyCache = null;

	private static Configuration instance;

	private int loginMethod = LOGIN_NOT_SET;
	private String login;
	private String password;
	private String sha1Password;
	private String authorId;
	private static SSLSocketFactory tlsFactory = null;
	/** no specific handling is needed for a normal tcp socket */
	private static SocketFactory tcpFactory = SocketFactory.getDefault();
	private SSLSocket atnaSocket = null;

	public Configuration() {
	}

	public String getAuthorId() {
		return authorId;
	}

	public void setAuthorId(String authorId) {
		this.authorId = authorId;
	}

	/**
	 * Provides a reference to the property cache. If it is not yet existent, it will automatically be created.
	 * 
	 * @return Reference to the property cache
	 */
	private HashMap<String, String> getPropertyCache() {
		if (this.propertyCache == null) {
			this.propertyCache = new HashMap<String, String>();
		}

		return this.propertyCache;
	}

	/**
	 * Provides a web-service url. If it is not yet cached, the url will be stored in a cache for further accelerated access
	 * 
	 * @param propertyName
	 *            The key for the web-service url
	 * @return The webservice-url or null if no corresponding url was found in the database
	 */
	public String getServiceUrl(String propertyName) {
		// first check if it is already in cache
		String serviceUrl = getPropertyCache().get(propertyName);
		if (serviceUrl == null) {
			// nope, it isn't. Thus try to fetch it from db
			serviceUrl = ESanteConfigManagerBean.getInstance().getESantePropertyValue(propertyName);
			// cache it for next usage
			getPropertyCache().put(propertyName, serviceUrl);
		}

		return serviceUrl;
	}

	/**
	 * ODO: SHOULD BE MOVED TO SENDER
	 * Sets the reference to the socket factory
	 * 
	 * @param socketFactory
	 *            Reference to the socket factory
	 * @throws Exception 
	 */
	public void setSocketFactory(SSLSocketFactory socketFactory) throws Exception {
		
		// set the socket factory
		this.tlsFactory = socketFactory;

		String atnaServer = null;
		try {
			// also directly create a socket for ATNA syslog communication
			atnaServer = getServiceUrl(ESanteProperty.PROP_SERVICE_ATNA);
			URL url = new URL(atnaServer);
			setAtnaSocket((SSLSocket) socketFactory.createSocket(url.getHost(), url.getPort()));
		} catch (MalformedURLException e) {
			setAtnaSocket(null);
			if ((atnaServer == null) || (atnaServer.length() == 0)) {
				logger.info("No ATNA server URL has been configured. ATNA logging is disabled.");
			} else {
				throw new Exception("The url for communicating with the ATNA service (\"" + getServiceUrl(ESanteProperty.PROP_SERVICE_ATNA) + "\") is invalid");
			}
		}
		catch(ConnectException conex){
			throw new Exception("The service for communicating with the ATNA service (\""+getServiceUrl(ESanteProperty.PROP_SERVICE_ATNA)+"\") refused the connection");
		}
		
		// inform the syslog server that the user now logged in the system
		String t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_START_APP);

		t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
		t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());	
		t = t.replace(WebserviceConstants.PLACEHOLDER_USER_ID, Integer.toString(MainFrame.getCurrentUserId()));

		SOAPUtilities.sendAtnaMessage(t, "LOGIN");
	}
	
	/**
	 * Provides the socket that should be used for atna communication
	 * 
	 * @return The atna socket
	 */
	public SSLSocket getAtnaSocket() {
		return atnaSocket;
	}

	/**
	 * Indicates if ATNA messages can be sent
	 * 
	 * @return True, if the system is capable to send ATNA messages, false otherwise
	 */
	public boolean isAtnaEnabled() {
		return getAtnaSocket() != null;
	}

	/**
	 * Sets the socket that will be used for atna communication
	 * 
	 * @param atnaSocket
	 *            The socket
	 */
	private void setAtnaSocket(SSLSocket atnaSocket) {
		this.atnaSocket = atnaSocket;
	}


	/**
	 * Provides a reverence to the socket factory using the keystore of the user & accepting all server certificates
	 * 
	 * @return Reference to a socket factory for secured connections
	 */
	public SSLSocketFactory getTlsSocketFactory() {
		return tlsFactory;
	}
	
	/**
	 * Provides a reverence to the tcp socket factory 
	 * 
	 * @return Reference to a socket factory for unsecured connections
	 */
	public SocketFactory getTcpSocketFactory() {
		return tcpFactory;
	}	
	
	/**
	 * Sets the login method that has been chosen by the user
	 * 
	 * @param the
	 *            login method
	 */
	public void setLoginMethod(int method) {
		this.loginMethod = method;
	}

	/**
	 * Provides the login method that has been chosen by the user
	 * 
	 * @return The chosen login method (either {@link #LOGIN_SMARTCARD}, {@link #LOGIN_USERNAME_PASSWORD}, or {@link #LOGIN_SIGNING_SERVER})
	 */
	public int getLoginMethod() {
		return this.loginMethod;
	}

	/**
	 * @return the login
	 */
	public String getLogin() {
		return login;
		// return "tudor";
	}

	/**
	 * @param login
	 *            the login to set
	 */
	public void setLogin(String login) {
		this.login = login;
	}

	/**
	 * @return the password
	 */
	public String getSha1Password() {
		return sha1Password;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return this.password;
	}

	/**
	 * @param password
	 *            the password to set
	 */
	public void setSha1Password(String password) {
		this.sha1Password = password;
	}

	public void setClearPassword(String password) {
		try {
			this.sha1Password = Security.sha1(password.getBytes());
			this.password = password;
		} catch (Exception e) {
			logger.log(Level.ERROR, e.getMessage(), e);
			this.sha1Password = null;
		}
	}

	/**
	 * Provides an instance of the configuration class
	 * 
	 * @return Instance of the configuration class
	 */
	public static Configuration getFilledConfiguration() {
		if (instance == null) {
			instance = new Configuration();
		}

		return instance;
	}
}