package org.shoushitsu.util.asyncservice;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntFunction;

/**
 * Provides description of thread sets to be used by asynchronous services.
 */
public final class Threading {

	/**
	 * Use the specified amount of threads, creating them with the
	 * {@link Thread#Thread(Runnable) Thread(Runnable)} constructor.
	 *
	 * @param threadCount the amount of threads to use.
	 *
	 * @return a threading specification object.

	 * @throws IllegalArgumentException if {@code threadCount} is not positive.
	 */
	public static Threading defaultThreads(int threadCount) {
		return new Threading(threadCount, null);
	}

	/**
	 * Use the specified amount of threads, using the specified pattern to obtain thread names.
	 * Specifically, if {@code index} is the index of a thread to be created
	 * (from {@code 0}, inclusive, to {@code threadCount}, exclusive),
	 * then the name of the new thread will be the result of {@code String.format(namePattern, index)}.
	 * Note that an improper format string may result in {@link java.util.IllegalFormatException}s being thrown
	 * during the service creation, which will most likely abort the start-up of the service.
	 *
	 * @param threadCount the amount of threads to use.
	 * @param namePattern the pattern for thread names, to be passed as the first parameter to
	 * {@link String#format(String, Object...)}.
	 *
	 * @return a threading specification object.

	 * @throws IllegalArgumentException if {@code namePattern} is {@code null} or
	 * if {@code threadCount} is not positive.
	 */
	public static Threading formatThreadNames(int threadCount, String namePattern) {
		if (namePattern == null) {
			throw new IllegalArgumentException("namePattern must be non-null");
		}
		return new Threading(threadCount, index -> String.format(namePattern, index));
	}

	/**
	 * Use a single thread with the specified name.
	 *
	 * @param name the name of the thread.
	 *
	 * @return a threading specification object.
	 *
	 * @throws IllegalArgumentException if {@code name == null}.
	 */
	public static Threading singleThread(String name) {
		if (name == null) {
			throw new IllegalArgumentException("name must be non-null");
		}
		return new Threading(1, ix -> name);
	}

	final int threadCount;

	private final IntFunction<String> threadNameByIndex;

	/**
	 * @throws IllegalArgumentException if {@code threadCount} is not positive.
	 */
	private Threading(int threadCount, IntFunction<String> threadNameByIndex) {
		if (threadCount < 1) {
			throw new IllegalArgumentException("thread count must be positive");
		}
		this.threadCount = threadCount;
		this.threadNameByIndex = threadNameByIndex;
	}

	final ThreadFactory createThreadFactory() {
		if (threadNameByIndex == null) {
			return Thread::new;
		}
		AtomicInteger threadIndex = new AtomicInteger();
		return target -> new Thread(target, threadNameByIndex.apply(threadIndex.getAndIncrement()));
	}

}

@@ -36,16 +36,43 @@
 rvice;%0A%0A
+import java.util.Optional;%0A
 import j
@@ -731,16 +731,22 @@
 adCount,
+ null,
  null);%0A
@@ -1928,16 +1928,22 @@
 , index)
+, null
 );%0A%09%7D%0A%0A%09
@@ -2339,16 +2339,22 @@
  -%3E name
+, null
 );%0A%09%7D%0A%0A%09
@@ -2432,16 +2432,74 @@
 Index;%0A%0A
+%09private final Optional%3CClassLoader%3E contextClassLoader;%0A%0A
 %09/**%0A%09 *
@@ -2650,16 +2650,58 @@
 eByIndex
+, Optional%3CClassLoader%3E contextClassLoader
 ) %7B%0A%09%09if
@@ -2878,16 +2878,674 @@
 Index;%0A%09
+%09this.contextClassLoader = contextClassLoader;%0A%09%7D%0A%0A%09/**%0A%09 * Set a %7B@linkplain Thread#getContextClassLoader() context class loader%7D for the threading specification.%0A%09 *%0A%09 * @param classLoader the class loader to use for the created threads. To use the system class loader,%0A%09 * pass %7B@code null%7D (it will override the default context class loader setting mechanism).%0A%09 *%0A%09 * @return a threading specification object that has the same specs as this, except for the context class loader.%0A%09 */%0A%09public Threading withContextClassLoader(ClassLoader classLoader) %7B%0A%09%09return new Threading(this.threadCount, this.threadNameByIndex, Optional.ofNullable(classLoader));%0A%09
 %7D%0A%0A%09fina
@@ -3584,76 +3584,133 @@
 ory(
-) %7B%0A%09%09if (threadNameByIndex == null
 ) %7B%0A%09%09
-%09
 return 
-Thread::new;%0A%09%09%7D%0A%09%09
+new SpecThreadFactory();%0A%09%7D%0A%0A%09private final class SpecThreadFactory implements ThreadFactory %7B%0A%0A%09%09private final 
 Atom
@@ -3758,102 +3758,390 @@
 ();%0A
+%0A
 %09%09
-return target -%3E new Thread(target, threadNameByIndex.apply(threadIndex.getAndIncrement()));
+@Override%0A%09%09public Thread newThread(Runnable target) %7B%0A%09%09%09Thread thread;%0A%09%09%09if (threadNameByIndex == null) %7B%0A%09%09%09%09thread = new Thread(target);%0A%09%09%09%7D else %7B%0A%09%09%09%09thread = new Thread(target, threadNameByIndex.apply(threadIndex.getAndIncrement()));%0A%09%09%09%7D%0A%09%09%09if (contextClassLoader != null) %7B%0A%09%09%09%09thread.setContextClassLoader(contextClassLoader.orElse(null));%0A%09%09%09%7D%0A%09%09%09return thread;%0A%09%09%7D%0A
 %0A%09%7D%0A

add Threading.withContextClassLoader()