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

package com.google.crypto.tink.util;

import java.util.concurrent.Executors;
import java.net.MalformedURLException;
import java.util.Locale;
import java.net.URL;
import java.util.regex.Matcher;
import com.google.api.client.http.HttpHeaders;
import java.io.BufferedReader;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.InputStream;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpRequest;
import java.io.Reader;
import java.io.InputStreamReader;
import com.google.api.client.http.GenericUrl;
import java.io.IOException;
import javax.annotation.concurrent.GuardedBy;
import com.google.api.client.http.HttpTransport;
import java.util.regex.Pattern;
import java.util.concurrent.Executor;
import com.google.api.client.http.javanet.NetHttpTransport;
import java.nio.charset.Charset;

@Deprecated
public class KeysDownloader
{
    private static final Charset UTF_8;
    private static final NetHttpTransport DEFAULT_HTTP_TRANSPORT;
    private static final Executor DEFAULT_BACKGROUND_EXECUTOR;
    private static final Pattern MAX_AGE_PATTERN;
    private final Executor backgroundExecutor;
    private final HttpTransport httpTransport;
    private final Object fetchDataLock;
    private final Object instanceStateLock;
    private final String url;
    @GuardedBy("instanceStateLock")
    private Runnable pendingRefreshRunnable;
    @GuardedBy("instanceStateLock")
    private String cachedData;
    @GuardedBy("instanceStateLock")
    private long cachedTimeInMillis;
    @GuardedBy("instanceStateLock")
    private long cacheExpirationDurationInMillis;
    
    public KeysDownloader(final Executor backgroundExecutor, final HttpTransport httpTransport, final String url) {
        validate(url);
        this.backgroundExecutor = backgroundExecutor;
        this.httpTransport = httpTransport;
        this.instanceStateLock = new Object();
        this.fetchDataLock = new Object();
        this.url = url;
        this.cachedTimeInMillis = Long.MIN_VALUE;
        this.cacheExpirationDurationInMillis = 0L;
    }
    
    public String download() throws IOException {
        synchronized (this.instanceStateLock) {
            if (this.hasNonExpiredDataCached()) {
                if (this.shouldProactivelyRefreshDataInBackground()) {
                    this.refreshInBackground();
                }
                return this.cachedData;
            }
        }
        synchronized (this.fetchDataLock) {
            synchronized (this.instanceStateLock) {
                if (this.hasNonExpiredDataCached()) {
                    return this.cachedData;
                }
            }
            return this.fetchAndCacheData();
        }
    }
    
    public HttpTransport getHttpTransport() {
        return this.httpTransport;
    }
    
    public String getUrl() {
        return this.url;
    }
    
    @GuardedBy("instanceStateLock")
    private boolean hasNonExpiredDataCached() {
        final long currentTimeInMillis = this.getCurrentTimeInMillis();
        final boolean cachedInFuture = this.cachedTimeInMillis > currentTimeInMillis;
        final boolean cacheExpired = this.cachedTimeInMillis + this.cacheExpirationDurationInMillis <= currentTimeInMillis;
        return !cacheExpired && !cachedInFuture;
    }
    
    @GuardedBy("instanceStateLock")
    private boolean shouldProactivelyRefreshDataInBackground() {
        return this.cachedTimeInMillis + this.cacheExpirationDurationInMillis / 2L <= this.getCurrentTimeInMillis();
    }
    
    long getCurrentTimeInMillis() {
        return System.currentTimeMillis();
    }
    
    @GuardedBy("fetchDataLock")
    @CanIgnoreReturnValue
    private String fetchAndCacheData() throws IOException {
        final long currentTimeInMillis = this.getCurrentTimeInMillis();
        final HttpRequest httpRequest = this.httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(this.url));
        final HttpResponse httpResponse = httpRequest.execute();
        if (httpResponse.getStatusCode() != 200) {
            throw new IOException("Unexpected status code = " + httpResponse.getStatusCode());
        }
        final InputStream contentStream = httpResponse.getContent();
        String data;
        try {
            final InputStreamReader reader = new InputStreamReader(contentStream, KeysDownloader.UTF_8);
            data = readerToString(reader);
        }
        finally {
            contentStream.close();
        }
        synchronized (this.instanceStateLock) {
            this.cachedTimeInMillis = currentTimeInMillis;
            this.cacheExpirationDurationInMillis = this.getExpirationDurationInSeconds(httpResponse.getHeaders()) * 1000L;
            this.cachedData = data;
        }
        return data;
    }
    
    private static String readerToString(Reader reader) throws IOException {
        reader = new BufferedReader(reader);
        final StringBuilder stringBuilder = new StringBuilder();
        int c;
        while ((c = reader.read()) != -1) {
            stringBuilder.append((char)c);
        }
        return stringBuilder.toString();
    }
    
    long getExpirationDurationInSeconds(final HttpHeaders httpHeaders) {
        long expirationDurationInSeconds = 0L;
        if (httpHeaders.getCacheControl() != null) {
            for (final String arg : httpHeaders.getCacheControl().split(",")) {
                final Matcher m = KeysDownloader.MAX_AGE_PATTERN.matcher(arg);
                if (m.matches()) {
                    expirationDurationInSeconds = Long.valueOf(m.group(1));
                    break;
                }
            }
        }
        if (httpHeaders.getAge() != null) {
            expirationDurationInSeconds -= httpHeaders.getAge();
        }
        return Math.max(0L, expirationDurationInSeconds);
    }
    
    public void refreshInBackground() {
        final Runnable refreshRunnable = this.newRefreshRunnable();
        synchronized (this.instanceStateLock) {
            if (this.pendingRefreshRunnable != null) {
                return;
            }
            this.pendingRefreshRunnable = refreshRunnable;
        }
        try {
            this.backgroundExecutor.execute(refreshRunnable);
        }
        catch (final Throwable e) {
            synchronized (this.instanceStateLock) {
                if (this.pendingRefreshRunnable == refreshRunnable) {
                    this.pendingRefreshRunnable = null;
                }
            }
            throw e;
        }
    }
    
    private Runnable newRefreshRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                synchronized (KeysDownloader.this.fetchDataLock) {
                    try {
                        KeysDownloader.this.fetchAndCacheData();
                    }
                    catch (final IOException ex) {
                        synchronized (KeysDownloader.this.instanceStateLock) {
                            if (KeysDownloader.this.pendingRefreshRunnable == this) {
                                KeysDownloader.this.pendingRefreshRunnable = null;
                            }
                        }
                    }
                    finally {
                        synchronized (KeysDownloader.this.instanceStateLock) {
                            if (KeysDownloader.this.pendingRefreshRunnable == this) {
                                KeysDownloader.this.pendingRefreshRunnable = null;
                            }
                        }
                    }
                }
            }
        };
    }
    
    private static void validate(final String url) {
        try {
            final URL tmp = new URL(url);
            if (!tmp.getProtocol().toLowerCase(Locale.US).equals("https")) {
                throw new IllegalArgumentException("url must point to a HTTPS server");
            }
        }
        catch (final MalformedURLException ex) {
            throw new IllegalArgumentException(ex);
        }
    }
    
    static {
        UTF_8 = Charset.forName("UTF-8");
        DEFAULT_HTTP_TRANSPORT = new NetHttpTransport.Builder().build();
        DEFAULT_BACKGROUND_EXECUTOR = Executors.newCachedThreadPool();
        MAX_AGE_PATTERN = Pattern.compile("\\s*max-age\\s*=\\s*(\\d+)\\s*");
    }
    
    public static class Builder
    {
        private HttpTransport httpTransport;
        private Executor executor;
        private String url;
        
        public Builder() {
            this.httpTransport = (HttpTransport)KeysDownloader.DEFAULT_HTTP_TRANSPORT;
            this.executor = KeysDownloader.DEFAULT_BACKGROUND_EXECUTOR;
        }
        
        @CanIgnoreReturnValue
        public Builder setUrl(final String val) {
            this.url = val;
            return this;
        }
        
        @CanIgnoreReturnValue
        public Builder setExecutor(final Executor val) {
            this.executor = val;
            return this;
        }
        
        @CanIgnoreReturnValue
        public Builder setHttpTransport(final HttpTransport httpTransport) {
            this.httpTransport = httpTransport;
            return this;
        }
        
        public KeysDownloader build() {
            if (this.url == null) {
                throw new IllegalArgumentException("must provide a url with {#setUrl}");
            }
            return new KeysDownloader(this.executor, this.httpTransport, this.url);
        }
    }
}
