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

package io.sentry;

import java.util.concurrent.CancellationException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.annotations.Nullable;
import io.sentry.util.AutoClosableReentrantLock;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class SentryExecutorService implements ISentryExecutorService
{
    private static final int INITIAL_QUEUE_SIZE = 40;
    private static final int MAX_QUEUE_SIZE = 271;
    @NotNull
    private final ScheduledThreadPoolExecutor executorService;
    @NotNull
    private final AutoClosableReentrantLock lock;
    @NotNull
    private final Runnable dummyRunnable;
    @Nullable
    private final SentryOptions options;
    
    @TestOnly
    SentryExecutorService(@NotNull final ScheduledThreadPoolExecutor executorService, @Nullable final SentryOptions options) {
        this.lock = new AutoClosableReentrantLock();
        this.dummyRunnable = (() -> {});
        this.executorService = executorService;
        this.options = options;
    }
    
    public SentryExecutorService(@Nullable final SentryOptions options) {
        this(new ScheduledThreadPoolExecutor(1, new SentryExecutorServiceThreadFactory()), options);
    }
    
    public SentryExecutorService() {
        this(new ScheduledThreadPoolExecutor(1, new SentryExecutorServiceThreadFactory()), null);
    }
    
    private boolean isQueueAvailable() {
        if (this.executorService.getQueue().size() >= 271) {
            this.executorService.purge();
        }
        return this.executorService.getQueue().size() < 271;
    }
    
    @NotNull
    @Override
    public Future<?> submit(@NotNull final Runnable runnable) throws RejectedExecutionException {
        if (this.isQueueAvailable()) {
            return this.executorService.submit(runnable);
        }
        if (this.options != null) {
            this.options.getLogger().log(SentryLevel.WARNING, "Task " + runnable + " rejected from " + this.executorService, new Object[0]);
        }
        return new CancelledFuture<Object>();
    }
    
    @NotNull
    @Override
    public <T> Future<T> submit(@NotNull final Callable<T> callable) throws RejectedExecutionException {
        if (this.isQueueAvailable()) {
            return this.executorService.submit(callable);
        }
        if (this.options != null) {
            this.options.getLogger().log(SentryLevel.WARNING, "Task " + callable + " rejected from " + this.executorService, new Object[0]);
        }
        return new CancelledFuture<T>();
    }
    
    @NotNull
    @Override
    public Future<?> schedule(@NotNull final Runnable runnable, final long delayMillis) throws RejectedExecutionException {
        return this.executorService.schedule(runnable, delayMillis, TimeUnit.MILLISECONDS);
    }
    
    @Override
    public void close(final long timeoutMillis) {
        try (final ISentryLifecycleToken ignored = this.lock.acquire()) {
            if (!this.executorService.isShutdown()) {
                this.executorService.shutdown();
                try {
                    if (!this.executorService.awaitTermination(timeoutMillis, TimeUnit.MILLISECONDS)) {
                        this.executorService.shutdownNow();
                    }
                }
                catch (final InterruptedException e) {
                    this.executorService.shutdownNow();
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
    
    @Override
    public boolean isClosed() {
        try (final ISentryLifecycleToken ignored = this.lock.acquire()) {
            return this.executorService.isShutdown();
        }
    }
    
    @Override
    public void prewarm() {
        try {
            this.executorService.submit(() -> {
                try {
                    for (int i = 0; i < 40; ++i) {
                        final ScheduledFuture<?> future = this.executorService.schedule(this.dummyRunnable, 365L, TimeUnit.DAYS);
                        future.cancel(true);
                    }
                    this.executorService.purge();
                }
                catch (final RejectedExecutionException ex) {}
            });
        }
        catch (final RejectedExecutionException e) {
            if (this.options != null) {
                this.options.getLogger().log(SentryLevel.WARNING, "Prewarm task rejected from " + this.executorService, e);
            }
        }
    }
    
    private static final class SentryExecutorServiceThreadFactory implements ThreadFactory
    {
        private int cnt;
        
        @NotNull
        @Override
        public Thread newThread(@NotNull final Runnable r) {
            final Thread ret = new Thread(r, "SentryExecutorServiceThreadFactory-" + this.cnt++);
            ret.setDaemon(true);
            return ret;
        }
    }
    
    private static final class CancelledFuture<T> implements Future<T>
    {
        @Override
        public boolean cancel(final boolean mayInterruptIfRunning) {
            return true;
        }
        
        @Override
        public boolean isCancelled() {
            return true;
        }
        
        @Override
        public boolean isDone() {
            return true;
        }
        
        @Override
        public T get() {
            throw new CancellationException();
        }
        
        @Override
        public T get(final long timeout, @NotNull final TimeUnit unit) {
            throw new CancellationException();
        }
    }
}
