/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
 */
package org.jenetics.util;

import static java.lang.Math.max;
import static java.util.Objects.requireNonNull;

import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;

import org.jenetics.internal.util.Context;

/**
 * [code]
 * try (Scoped<Executor> executor = Concurrent.scope()) {
 *     executor.get().execute(task1);
 *     executor.get().execute(task2);
 * }
 * [/code]
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 * @since 2.0
 * @version 2.0 &mdash; <em>$Date: 2014-03-22 $</em>
 */
public abstract class Concurrent implements Executor {

	private static final int MIN_THRESHOLD = 2;
	private static final int CORES = Runtime.getRuntime().availableProcessors();

	private static final ForkJoinPool DEFAULT = new ForkJoinPool(
		Math.max(Runtime.getRuntime().availableProcessors() - 1, 1)
	);

	private static final Concurrent SERIAL_EXECUTOR = new Concurrent() {
		@Override
		public void execute(final Runnable command) {
			command.run();
		}
		@Override
		public void execute(final List<? extends Runnable> runnables) {
			for (final Runnable runnable : runnables) {
				runnable.run();
			}
		}
	};

	private static final Context<Executor> CONTEXT =
		new Context<Executor>(DEFAULT);

	Concurrent() {
	}

	/**
	 * Set the executor object to use for concurrent actions.
	 *
	 * @param executor the executor object to use.
	 * @throws java.lang.NullPointerException if the given {@code executor} is
	 *         {@code null}.
	 */
	public static void setExecutor(final Executor executor) {
		CONTEXT.set(requireNonNull(executor, "Executor must not be null."));
	}

	/**
	 * Return the currently used executor object.
	 *
	 * @return the currently used thread pool.
	 */
	public static Executor getExecutor() {
		return CONTEXT.get();
	}

	/**
	 * Open a new executor scope with the currently set
	 * {@link java.util.concurrent.Executor}.
	 *
	 * @return a new executor with the currently se {@code Executor}.
	 */
	public static Scoped<Concurrent> scope() {
		return newScope(CONTEXT.get());
	}

	/**
	 * Open a new executor scope, where all tasks are executed in the main
	 * thread.
	 *
	 * @return a new <i>serial</i> executor scope.
	 */
	public static Scoped<Concurrent> serial() {
		return CONTEXT.scope(SERIAL_EXECUTOR);
	}

	/**
	 * Open a new executor scope with the given {@code executor}. When this
	 * scope is <i>closed</i>, the original executor is restored.
	 *
	 * @param executor the executor used for the new scope.
	 * @return a new executor scope
	 * @throws java.lang.NullPointerException if the given {@code executor} is
	 *         {@code null}.
	 */
	public static Scoped<Concurrent> scope(final Executor executor) {
		final Scoped<Executor> scope = CONTEXT.scope(requireNonNull(executor));
		final Scoped<Concurrent> exec = newScope(executor);

		return new ScopedExecutorProxy(scope, exec);
	}

	/**
	 * Set the used executor to the default value.
	 */
	public static void reset() {
		CONTEXT.reset();
	}

	private static Scoped<Concurrent> newScope(final Executor executor) {
		if (executor instanceof ForkJoinPool) {
			return new ScopedForkJoinPool((ForkJoinPool)executor);
		} else if (executor instanceof ExecutorService) {
			return new ScopedExecutorService((ExecutorService)executor);
		} else {
			return new ScopedExecutor(executor);
		}
	}

	/* *************************************************************************
	 *  Instance methods
	 * ************************************************************************/

	public void execute(final List<? extends Runnable> runnables) {
		final int[] parts = arrays.partition(
			runnables.size(),
			partitions(runnables.size())
		);

		for (int i = 0; i < parts.length - 1; ++i) {
			final int part = i;

			execute(new Runnable() { @Override public void run() {
				for (int j = parts[part]; j < parts[part + 1]; ++j) {
					runnables.get(j).run();
				}
			}});
		}
	}

	private static int partitions(final int ntasks) {
		int threshold;
		if (CORES == 1) {
			threshold = max(ntasks/2, MIN_THRESHOLD);
		} else {
			threshold = max(
				(int)((double)ntasks/(CORES*2)),
				MIN_THRESHOLD
			);
		}

		return max(ntasks/threshold, 1);
	}
}

@@ -1071,26 +1071,30 @@
 ped%3C
-Executor%3E executor
+Concurrent%3E concurrent
  = C
@@ -1117,32 +1117,34 @@
 )) %7B%0A *     
-executor
+concurrent
 .get().execu
@@ -1161,24 +1161,26 @@
 %0A *     
-executor
+concurrent
 .get().e
@@ -1426,53 +1426,8 @@
  %7B%0A%0A
-%09private static final int MIN_THRESHOLD = 2;%0A
 %09pri
@@ -1545,17 +1545,19 @@
 EFAULT =
- 
+%0A%09%09
 new Fork
@@ -1569,70 +1569,25 @@
 ool(
-%0A%09%09Math.max(Runtime.getRuntime().availableProcessors()
+max(CORES
  - 1, 1)
 %0A%09);
@@ -1582,18 +1582,16 @@
  - 1, 1)
-%0A%09
 );%0A%0A%09pri
@@ -4385,36 +4385,34 @@
 %0A%09%09%09
-partitions(runnables.size())
+CORES == 1 ? 1 : CORES + 1
 %0A%09%09)
@@ -4484,17 +4484,16 @@
 rt = i;%0A
-%0A
 %09%09%09execu
@@ -4655,274 +4655,6 @@
 %09%7D%0A%0A
-%09private static int partitions(final int ntasks) %7B%0A%09%09int threshold;%0A%09%09if (CORES == 1) %7B%0A%09%09%09threshold = max(ntasks/2, MIN_THRESHOLD);%0A%09%09%7D else %7B%0A%09%09%09threshold = max(%0A%09%09%09%09(int)((double)ntasks/(CORES*2)),%0A%09%09%09%09MIN_THRESHOLD%0A%09%09%09);%0A%09%09%7D%0A%0A%09%09return max(ntasks/threshold, 1);%0A%09%7D%0A
 %7D%0A

Optimize concurrency.