package com.atlassian.bitbucket.mesh.util;

import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import javax.annotation.Nonnull;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;

public class ConcurrencyUtils {

    private ConcurrencyUtils() {
        throw new UnsupportedOperationException(getClass().getName() + " is a utility class");
    }

    /**
     * Creates a new {@link ThreadFactory} which will create <i>daemon</i> threads with names starting with
     * {@code prefix + ":thread-"}, followed by an incrementing counter. The ":thread-" part is added to the
     * provided {@code prefix} to match how Fugue's {@code ThreadFactories}-generated factories work.
     *
     * @param prefix the prefix for generated thread names, to which {@code ":thread-"} and a counter are appended
     * @return the created thread factory
     */
    @Nonnull
    public static ThreadFactory createDaemonThreadFactory(@Nonnull String prefix) {
        CustomizableThreadFactory threadFactory = new CustomizableThreadFactory();
        threadFactory.setDaemon(true);
        threadFactory.setThreadNamePrefix(prefix + ":thread-");

        return threadFactory;
    }

    /**
     * Creates a {@link ScheduledExecutorService} which uses a <i>single daemon thread</i> whose name will be
     * {@code prefix + ":thread-1"}.
     *
     * @param prefix      the prefix for generated thread names, to which {@code ":thread-"} and a counter are appended
     * @return the created executor
     */
    @Nonnull
    public static ScheduledExecutorService createScheduledExecutor(@Nonnull String prefix) {
        return createScheduledExecutor(createDaemonThreadFactory(prefix));
    }

    /**
     * Creates a {@link ScheduledExecutorService} which uses a <i>single thread</i> created by the provided
     * {@link ThreadFactory}.
     *
     * @param threadFactory a factory to create the executor's thread
     * @return the created executor
     */
    @Nonnull
    public static ScheduledExecutorService createScheduledExecutor(@Nonnull ThreadFactory threadFactory) {
        return createScheduledExecutor(1, threadFactory);
    }

    /**
     * Creates a {@link ScheduledExecutorService} which uses the specified number of threads created by the
     * provided {@link ThreadFactory}.
     *
     * @param coreThreads   the number of threads the executor should use
     * @param threadFactory a factory to create the executor's thread
     * @return the created executor
     */
    @Nonnull
    public static ScheduledExecutorService createScheduledExecutor(int coreThreads,
                                                                   @Nonnull ThreadFactory threadFactory) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(coreThreads, threadFactory);
        //Apply some best-practice configuration. In particular, removeOnCancelPolicy = true is important
        //for avoiding memory leaks if scheduled tasks are frequently canceled
        executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
        executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
        executor.setRemoveOnCancelPolicy(true);

        return executor;
    }
}
