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

package io.netty.handler.ssl;

import io.netty.util.AsciiString;
import java.util.Iterator;
import java.util.List;
import io.netty.internal.tcnative.SSL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Set;
import java.util.Map;

final class OpenSslClientSessionCache extends OpenSslSessionCache
{
    private final Map<HostPort, Set<NativeSslSession>> sessions;
    
    OpenSslClientSessionCache(final Map<Long, ReferenceCountedOpenSslEngine> engines) {
        super(engines);
        this.sessions = new HashMap<HostPort, Set<NativeSslSession>>();
    }
    
    @Override
    protected boolean sessionCreated(final NativeSslSession session) {
        assert Thread.holdsLock(this);
        final HostPort hostPort = keyFor(session.getPeerHost(), session.getPeerPort());
        if (hostPort == null) {
            return false;
        }
        Set<NativeSslSession> sessionsForHost = this.sessions.get(hostPort);
        if (sessionsForHost == null) {
            sessionsForHost = new HashSet<NativeSslSession>(4);
            this.sessions.put(hostPort, sessionsForHost);
        }
        sessionsForHost.add(session);
        return true;
    }
    
    @Override
    protected void sessionRemoved(final NativeSslSession session) {
        assert Thread.holdsLock(this);
        final HostPort hostPort = keyFor(session.getPeerHost(), session.getPeerPort());
        if (hostPort == null) {
            return;
        }
        final Set<NativeSslSession> sessionsForHost = this.sessions.get(hostPort);
        if (sessionsForHost != null) {
            sessionsForHost.remove(session);
            if (sessionsForHost.isEmpty()) {
                this.sessions.remove(hostPort);
            }
        }
    }
    
    @Override
    boolean setSession(final long ssl, final OpenSslInternalSession session, final String host, final int port) {
        final HostPort hostPort = keyFor(host, port);
        if (hostPort == null) {
            return false;
        }
        NativeSslSession nativeSslSession = null;
        boolean singleUsed = false;
        final boolean reused;
        synchronized (this) {
            final Set<NativeSslSession> sessionsForHost = this.sessions.get(hostPort);
            if (sessionsForHost == null) {
                return false;
            }
            if (sessionsForHost.isEmpty()) {
                this.sessions.remove(hostPort);
                return false;
            }
            List<NativeSslSession> toBeRemoved = null;
            for (final NativeSslSession sslSession : sessionsForHost) {
                if (sslSession.isValid()) {
                    nativeSslSession = sslSession;
                    break;
                }
                if (toBeRemoved == null) {
                    toBeRemoved = new ArrayList<NativeSslSession>(2);
                }
                toBeRemoved.add(sslSession);
            }
            if (toBeRemoved != null) {
                for (final NativeSslSession sslSession : toBeRemoved) {
                    this.removeSessionWithId(sslSession.sessionId());
                }
            }
            if (nativeSslSession == null) {
                return false;
            }
            reused = SSL.setSession(ssl, nativeSslSession.session());
            if (reused) {
                singleUsed = nativeSslSession.shouldBeSingleUse();
            }
        }
        if (reused) {
            if (singleUsed) {
                nativeSslSession.invalidate();
                session.invalidate();
            }
            nativeSslSession.setLastAccessedTime(System.currentTimeMillis());
            session.setSessionDetails(nativeSslSession.getCreationTime(), nativeSslSession.getLastAccessedTime(), nativeSslSession.sessionId(), nativeSslSession.keyValueStorage);
        }
        return reused;
    }
    
    private static HostPort keyFor(final String host, final int port) {
        if (host == null && port < 1) {
            return null;
        }
        return new HostPort(host, port);
    }
    
    @Override
    synchronized void clear() {
        super.clear();
        this.sessions.clear();
    }
    
    private static final class HostPort
    {
        private final int hash;
        private final String host;
        private final int port;
        
        HostPort(final String host, final int port) {
            this.host = host;
            this.port = port;
            this.hash = 31 * AsciiString.hashCode(host) + port;
        }
        
        @Override
        public int hashCode() {
            return this.hash;
        }
        
        @Override
        public boolean equals(final Object obj) {
            if (!(obj instanceof HostPort)) {
                return false;
            }
            final HostPort other = (HostPort)obj;
            return this.port == other.port && this.host.equalsIgnoreCase(other.host);
        }
        
        @Override
        public String toString() {
            return "HostPort{host='" + this.host + '\'' + ", port=" + this.port + '}';
        }
    }
}
