// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.handler.codec.quic;

import java.net.Socket;
import java.security.Principal;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import io.netty.handler.ssl.util.KeyManagerFactoryWrapper;
import java.security.cert.Certificate;
import java.security.Key;
import java.security.KeyStore;
import io.netty.handler.ssl.util.TrustManagerFactoryWrapper;
import javax.net.ssl.TrustManager;
import io.netty.util.internal.ObjectUtil;
import java.util.HashMap;
import javax.net.ssl.KeyManager;
import java.security.cert.X509Certificate;
import java.security.PrivateKey;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import io.netty.util.Mapping;
import io.netty.handler.ssl.ClientAuth;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import io.netty.handler.ssl.SslContextOption;
import java.util.Map;
import javax.net.ssl.X509ExtendedKeyManager;

public final class QuicSslContextBuilder
{
    private static final X509ExtendedKeyManager SNI_KEYMANAGER;
    private static final Map.Entry[] EMPTY_ENTRIES;
    private final boolean forServer;
    private final Map<SslContextOption<?>, Object> options;
    private TrustManagerFactory trustManagerFactory;
    private String keyPassword;
    private KeyManagerFactory keyManagerFactory;
    private long sessionCacheSize;
    private long sessionTimeout;
    private ClientAuth clientAuth;
    private String[] applicationProtocols;
    private Boolean earlyData;
    private BoringSSLKeylog keylog;
    private Mapping<? super String, ? extends QuicSslContext> mapping;
    
    public static QuicSslContextBuilder forClient() {
        return new QuicSslContextBuilder(false);
    }
    
    public static QuicSslContextBuilder forServer(final File keyFile, @Nullable final String keyPassword, final File certChainFile) {
        return new QuicSslContextBuilder(true).keyManager(keyFile, keyPassword, certChainFile);
    }
    
    public static QuicSslContextBuilder forServer(final PrivateKey key, @Nullable final String keyPassword, final X509Certificate... certChain) {
        return new QuicSslContextBuilder(true).keyManager(key, keyPassword, certChain);
    }
    
    public static QuicSslContextBuilder forServer(final KeyManagerFactory keyManagerFactory, @Nullable final String password) {
        return new QuicSslContextBuilder(true).keyManager(keyManagerFactory, password);
    }
    
    public static QuicSslContextBuilder forServer(final KeyManager keyManager, @Nullable final String keyPassword) {
        return new QuicSslContextBuilder(true).keyManager(keyManager, keyPassword);
    }
    
    public static QuicSslContext buildForServerWithSni(final Mapping<? super String, ? extends QuicSslContext> mapping) {
        return forServer(QuicSslContextBuilder.SNI_KEYMANAGER, null).sni(mapping).build();
    }
    
    private QuicSslContextBuilder(final boolean forServer) {
        this.options = new HashMap<SslContextOption<?>, Object>();
        this.sessionCacheSize = 20480L;
        this.sessionTimeout = 300L;
        this.clientAuth = ClientAuth.NONE;
        this.forServer = forServer;
    }
    
    private QuicSslContextBuilder sni(final Mapping<? super String, ? extends QuicSslContext> mapping) {
        this.mapping = ObjectUtil.checkNotNull(mapping, "mapping");
        return this;
    }
    
    public <T> QuicSslContextBuilder option(final SslContextOption<T> option, final T value) {
        if (value == null) {
            this.options.remove(option);
        }
        else {
            this.options.put(option, value);
        }
        return this;
    }
    
    public QuicSslContextBuilder earlyData(final boolean enabled) {
        this.earlyData = enabled;
        return this;
    }
    
    public QuicSslContextBuilder keylog(final boolean enabled) {
        this.keylog(enabled ? BoringSSLLoggingKeylog.INSTANCE : null);
        return this;
    }
    
    public QuicSslContextBuilder keylog(@Nullable final BoringSSLKeylog keylog) {
        this.keylog = keylog;
        return this;
    }
    
    public QuicSslContextBuilder trustManager(@Nullable final File trustCertCollectionFile) {
        try {
            return this.trustManager(QuicheQuicSslContext.toX509Certificates0(trustCertCollectionFile));
        }
        catch (final Exception e) {
            throw new IllegalArgumentException("File does not contain valid certificates: " + trustCertCollectionFile, e);
        }
    }
    
    public QuicSslContextBuilder trustManager(final X509Certificate... trustCertCollection) {
        try {
            return this.trustManager(QuicheQuicSslContext.buildTrustManagerFactory0(trustCertCollection));
        }
        catch (final Exception e) {
            throw new IllegalArgumentException(e);
        }
    }
    
    public QuicSslContextBuilder trustManager(@Nullable final TrustManagerFactory trustManagerFactory) {
        this.trustManagerFactory = trustManagerFactory;
        return this;
    }
    
    public QuicSslContextBuilder trustManager(final TrustManager trustManager) {
        return this.trustManager(new TrustManagerFactoryWrapper(trustManager));
    }
    
    public QuicSslContextBuilder keyManager(@Nullable final File keyFile, @Nullable final String keyPassword, @Nullable final File keyCertChainFile) {
        X509Certificate[] keyCertChain;
        try {
            keyCertChain = QuicheQuicSslContext.toX509Certificates0(keyCertChainFile);
        }
        catch (final Exception e) {
            throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e);
        }
        PrivateKey key;
        try {
            key = QuicheQuicSslContext.toPrivateKey0(keyFile, keyPassword);
        }
        catch (final Exception e) {
            throw new IllegalArgumentException("File does not contain valid private key: " + keyFile, e);
        }
        return this.keyManager(key, keyPassword, keyCertChain);
    }
    
    public QuicSslContextBuilder keyManager(@Nullable final PrivateKey key, @Nullable final String keyPassword, final X509Certificate... certChain) {
        try {
            final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null);
            final char[] pass = (keyPassword == null) ? new char[0] : keyPassword.toCharArray();
            ks.setKeyEntry("alias", key, pass, certChain);
            final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(ks, pass);
            return this.keyManager(keyManagerFactory, keyPassword);
        }
        catch (final Exception e) {
            throw new IllegalArgumentException(e);
        }
    }
    
    public QuicSslContextBuilder keyManager(@Nullable final KeyManagerFactory keyManagerFactory, @Nullable final String keyPassword) {
        this.keyPassword = keyPassword;
        this.keyManagerFactory = keyManagerFactory;
        return this;
    }
    
    public QuicSslContextBuilder keyManager(final KeyManager keyManager, @Nullable final String password) {
        return this.keyManager(new KeyManagerFactoryWrapper(keyManager), password);
    }
    
    public QuicSslContextBuilder applicationProtocols(final String... applicationProtocols) {
        this.applicationProtocols = applicationProtocols;
        return this;
    }
    
    public QuicSslContextBuilder sessionCacheSize(final long sessionCacheSize) {
        this.sessionCacheSize = sessionCacheSize;
        return this;
    }
    
    public QuicSslContextBuilder sessionTimeout(final long sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
        return this;
    }
    
    public QuicSslContextBuilder clientAuth(final ClientAuth clientAuth) {
        if (!this.forServer) {
            throw new UnsupportedOperationException("Only supported for server");
        }
        this.clientAuth = ObjectUtil.checkNotNull(clientAuth, "clientAuth");
        return this;
    }
    
    public QuicSslContext build() {
        if (this.forServer) {
            return new QuicheQuicSslContext(true, this.sessionTimeout, this.sessionCacheSize, this.clientAuth, this.trustManagerFactory, this.keyManagerFactory, this.keyPassword, this.mapping, this.earlyData, this.keylog, this.applicationProtocols, (Map.Entry<SslContextOption<?>, Object>[])toArray(this.options.entrySet(), QuicSslContextBuilder.EMPTY_ENTRIES));
        }
        return new QuicheQuicSslContext(false, this.sessionTimeout, this.sessionCacheSize, this.clientAuth, this.trustManagerFactory, this.keyManagerFactory, this.keyPassword, this.mapping, this.earlyData, this.keylog, this.applicationProtocols, (Map.Entry<SslContextOption<?>, Object>[])toArray(this.options.entrySet(), QuicSslContextBuilder.EMPTY_ENTRIES));
    }
    
    private static <T> T[] toArray(final Iterable<? extends T> iterable, final T[] prototype) {
        if (iterable == null) {
            return null;
        }
        final List<T> list = new ArrayList<T>();
        for (final T element : iterable) {
            list.add(element);
        }
        return list.toArray(prototype);
    }
    
    static {
        SNI_KEYMANAGER = new X509ExtendedKeyManager() {
            private final X509Certificate[] emptyCerts = new X509Certificate[0];
            private final String[] emptyStrings = new String[0];
            
            @Override
            public String[] getClientAliases(final String keyType, final Principal[] issuers) {
                return this.emptyStrings;
            }
            
            @Nullable
            @Override
            public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) {
                return null;
            }
            
            @Override
            public String[] getServerAliases(final String keyType, final Principal[] issuers) {
                return this.emptyStrings;
            }
            
            @Nullable
            @Override
            public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
                return null;
            }
            
            @Override
            public X509Certificate[] getCertificateChain(final String alias) {
                return this.emptyCerts;
            }
            
            @Nullable
            @Override
            public PrivateKey getPrivateKey(final String alias) {
                return null;
            }
        };
        EMPTY_ENTRIES = new Map.Entry[0];
    }
}
