/**
 * 
 */
package org.minnal.core.server;

import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslHandler;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.Security;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

import org.minnal.core.MinnalException;
import org.minnal.core.Router;
import org.minnal.core.config.ConnectorConfiguration;
import org.minnal.core.config.SSLConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author ganeshs
 *
 */
public class HttpsConnector extends AbstractHttpConnector {
	
	private static final Logger logger = LoggerFactory.getLogger(HttpsConnector.class);
	
	/**
	 * @param configuration
	 * @param router
	 */
	public HttpsConnector(ConnectorConfiguration configuration, Router router) {
		super(configuration, router);
		if (configuration.getSslConfiguration() == null) {
			logger.error("SSL configuration is missing for https connector");
			throw new MinnalException("SSL configuration is missing for https scheme");
		}
	}

	@Override
	protected void addChannelHandlers(ChannelPipeline pipeline) {
		logger.debug("Adding ssl handler to the pipeline");
		SSLEngine engine = createSslEngine();
		engine.setUseClientMode(false);
		pipeline.addFirst("ssl", new SslHandler(engine));
	}

	/**
	 * @return
	 */
	protected SSLEngine createSslEngine() {
		logger.debug("Creating a SSL engine from the SSL context");
		String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
		if (algorithm == null) {
			algorithm = "SunX509";
			logger.trace("ssl.KeyManagerFactory.algorithm algorithm is not set. Defaulting to {}", algorithm);
		}
		SSLContext serverContext = null;
		SSLConfiguration configuration = getConnectorConfiguration().getSslConfiguration();
		InputStream stream = null;
		try {
			File file = new File(configuration.getKeyStoreFile());
			stream = new FileInputStream(file);
			KeyStore ks = KeyStore.getInstance(configuration.getKeystoreType());
			ks.load(stream, configuration.getKeyStorePassword().toCharArray());

			// Set up key manager factory to use our key store
			KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
			kmf.init(ks, configuration.getKeyPassword().toCharArray());

			// Initialize the SSLContext to work with our key managers.
			serverContext = SSLContext.getInstance(configuration.getProtocol());
			serverContext.init(kmf.getKeyManagers(), null, null);
		} catch (Exception e) {
			logger.error("Failed while initializing the ssl context", e);
			throw new MinnalException("Failed to initialize the ssl context", e);
		} finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (IOException e) {
					logger.trace("Failed while closing the stream", e);
				}
			}
 		}
		return serverContext.createSSLEngine();
	}
}
