/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.camel.util.jsse;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A representation of configuration options for creating and loading
 * {@link KeyManager} instance(s).
 */
public class KeyManagersParameters extends JsseParameters {

    private static final Logger LOG = LoggerFactory.getLogger(KeyManagersParameters.class);

    /**
     * The key store configuration used to create the {@link KeyStoreParameters} that the
     * {@link KeyManager}s produced by this object's configuration expose.
     */
    protected KeyStoreParameters keyStore;
    
    /**
     * The optional password for recovering keys in the key store. Used by the
     * {@link KeyManagerFactory} that creates the {@link KeyManager}s
     * represented by this object's configuration.
     */
    protected String keyPassword;
    
    /**
     * The optional provider identifier for the {@link KeyManagerFactory} used to create
     * the {@link KeyManager}s represented by this object's configuration.
     */
    protected String provider;
    
    /**
     * The optional algorithm name for the {@link KeyManagerFactory} used to create
     * the {@link KeyManager}s represented by this object's configuration.  See the <a href=
     * "http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html"
     * >Java Secure Socket Extension Reference Guide</a> for information about
     * standard algorithm names.
     */
    protected String algorithm;

    /**
     * Creates {@link KeyManager}s based on this instance's configuration and the
     * {@code KeyStore} produced by the configuration returned from
     * {@link #getKeyStore()}. The {@code KeyManager}s are produced from a
     * factory created by using the provider and algorithm identifiers returned
     * by {@link #getProvider()} and {@link #getAlgorithm()}, respectively. If
     * either of these methods returns null, the default JSSE value is used
     * instead.
     * 
     * @return the initialized {@code KeyManager}s
     * @throws GeneralSecurityException if there is an error creating the
     *             {@code KeyManager}s or in creating the {@code KeyStore}
     * @throws IOException if there is an error loading the {@code KeyStore}
     *
     * @see KeyStoreParameters#createKeyStore()
     */
    public KeyManager[] createKeyManagers() throws GeneralSecurityException, IOException {
        
        LOG.trace("Creating KeyManager[] from KeyManagersParameters [{}].", this);

        KeyManager[] keyManagers;
        
        String kmfAlgorithm = this.parsePropertyValue(this.getAlgorithm());
        if (kmfAlgorithm == null) {
            kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
        }
        
        KeyManagerFactory kmf;
        if (this.getProvider() == null) {
            kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
        } else {
            kmf = KeyManagerFactory.getInstance(kmfAlgorithm, this.parsePropertyValue(this.getProvider()));
        }
        
        LOG.debug("KeyManagerFactory [{}], initialized from [{}], is using provider [{}] and algorithm [{}].",
                  new Object[] {kmf, this, kmf.getProvider(), kmf.getAlgorithm()});
        
        char[] kmfPassword = null;
        if (this.getKeyPassword() != null) {
            kmfPassword = this.parsePropertyValue(this.getKeyPassword()).toCharArray();
        }
        
        KeyStore ks = this.getKeyStore() == null ? null : this.getKeyStore().createKeyStore();
        
        kmf.init(ks, kmfPassword);
        keyManagers = kmf.getKeyManagers();
        
        LOG.debug("KeyManager[] [{}], initialized from KeyManagerFactory [{}].", keyManagers, kmf);
        
        return keyManagers;
    }

    /**
     * @see #setKeyStore(KeyStoreParameters)
     */
    public KeyStoreParameters getKeyStore() {
        return keyStore;
    }

    /**
     * Sets the key store configuration used to create the {@link KeyStore} that the
     * {@link KeyManager}s produced by this object's configuration expose.
     * 
     * @param value the configuration to use
     */
    public void setKeyStore(KeyStoreParameters value) {
        this.keyStore = value;
    }

    /**
     * @see #setKeyPassword(String)
     */
    public String getKeyPassword() {
        return keyPassword;
    }

    /**
     * Sets the optional password for recovering keys in the key store. Used by the
     * {@link KeyManagerFactory} that creates the {@link KeyManager}s
     * represented by this object's configuration.
     *
     * @param value the value to use
     */
    public void setKeyPassword(String value) {
        this.keyPassword = value;
    }

    /**
     * @see #setProvider(String)
     */
    public String getProvider() {
        return provider;
    }

    /**
     * Sets the optional provider identifier for the {@link KeyManagerFactory} used to create
     * the {@link KeyManager}s represented by this object's configuration.
     * 
     * @param value the desired provider identifier or {@code null} to use the
     *            highest priority provider implementing the algorithm
     *
     * @see Security#getProviders()
     */
    public void setProvider(String value) {
        this.provider = value;
    }

    /**
     * @see KeyManagerFactory#getDefaultAlgorithm()
     */
    public String getAlgorithm() {
        return algorithm;
    }

    /**
     * Sets optional algorithm name for the {@link KeyManagerFactory} used to create
     * the {@link KeyManager}s represented by this object's configuration.  See the <a href=
     * "http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html"
     * >Java Secure Socket Extension Reference Guide</a> for information about
     * standard algorithm names.
     * 
     * @param value the desired algorithm or {@code null} to use default
     *
     * @see KeyManagerFactory#getDefaultAlgorithm()
     */
    public void setAlgorithm(String value) {
        this.algorithm = value;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("KeyManagersParameters [keyStore=");
        builder.append(keyStore);
        builder.append(", keyPassword=");
        builder.append("********");
        builder.append(", provider=");
        builder.append(provider);
        builder.append(", algorithm=");
        builder.append(algorithm);
        builder.append(", getContext()=");
        builder.append(getCamelContext());
        builder.append("]");
        return builder.toString();
    }
}
