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

package io.sentry;

import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import io.sentry.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.net.InetAddress;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jetbrains.annotations.NotNull;
import io.sentry.util.AutoClosableReentrantLock;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class HostnameCache
{
    private static final long HOSTNAME_CACHE_DURATION;
    private static final long GET_HOSTNAME_TIMEOUT;
    @Nullable
    private static volatile HostnameCache INSTANCE;
    @NotNull
    private static final AutoClosableReentrantLock staticLock;
    private final long cacheDuration;
    @Nullable
    private volatile String hostname;
    private volatile long expirationTimestamp;
    @NotNull
    private final AtomicBoolean updateRunning;
    @NotNull
    private final Callable<InetAddress> getLocalhost;
    @NotNull
    private final ExecutorService executorService;
    
    @NotNull
    public static HostnameCache getInstance() {
        if (HostnameCache.INSTANCE == null) {
            try (final ISentryLifecycleToken ignored = HostnameCache.staticLock.acquire()) {
                if (HostnameCache.INSTANCE == null) {
                    HostnameCache.INSTANCE = new HostnameCache();
                }
            }
        }
        return HostnameCache.INSTANCE;
    }
    
    private HostnameCache() {
        this(HostnameCache.HOSTNAME_CACHE_DURATION);
    }
    
    HostnameCache(final long cacheDuration) {
        this(cacheDuration, () -> InetAddress.getLocalHost());
    }
    
    HostnameCache(final long cacheDuration, @NotNull final Callable<InetAddress> getLocalhost) {
        this.updateRunning = new AtomicBoolean(false);
        this.executorService = Executors.newSingleThreadExecutor(new HostnameCacheThreadFactory());
        this.cacheDuration = cacheDuration;
        this.getLocalhost = Objects.requireNonNull(getLocalhost, "getLocalhost is required");
        this.updateCache();
    }
    
    void close() {
        this.executorService.shutdown();
    }
    
    boolean isClosed() {
        return this.executorService.isShutdown();
    }
    
    @Nullable
    public String getHostname() {
        if (this.expirationTimestamp < System.currentTimeMillis() && this.updateRunning.compareAndSet(false, true)) {
            this.updateCache();
        }
        return this.hostname;
    }
    
    private void updateCache() {
        final Callable<Void> hostRetriever = (Callable<Void>)(() -> {
            try {
                this.hostname = this.getLocalhost.call().getCanonicalHostName();
                this.expirationTimestamp = System.currentTimeMillis() + this.cacheDuration;
            }
            finally {
                this.updateRunning.set(false);
            }
            return null;
        });
        try {
            final Future<Void> futureTask = this.executorService.submit(hostRetriever);
            futureTask.get(HostnameCache.GET_HOSTNAME_TIMEOUT, TimeUnit.MILLISECONDS);
        }
        catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
            this.handleCacheUpdateFailure();
        }
        catch (final ExecutionException | TimeoutException | RuntimeException e2) {
            this.handleCacheUpdateFailure();
        }
    }
    
    private void handleCacheUpdateFailure() {
        this.expirationTimestamp = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(1L);
    }
    
    static {
        HOSTNAME_CACHE_DURATION = TimeUnit.HOURS.toMillis(5L);
        GET_HOSTNAME_TIMEOUT = TimeUnit.SECONDS.toMillis(1L);
        staticLock = new AutoClosableReentrantLock();
    }
    
    private static final class HostnameCacheThreadFactory implements ThreadFactory
    {
        private int cnt;
        
        @NotNull
        @Override
        public Thread newThread(@NotNull final Runnable r) {
            final Thread ret = new Thread(r, "SentryHostnameCache-" + this.cnt++);
            ret.setDaemon(true);
            return ret;
        }
    }
}
