package com.hammurapi.reasoning.impl;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import com.hammurapi.config.bootstrap.ConfigurationException;
import com.hammurapi.config.bootstrap.Destroyable;
import com.hammurapi.flow.runtime.Invocable;
import com.hammurapi.flow.runtime.Invoker;
import com.hammurapi.flow.runtime.ProcessingPathElement;
import com.hammurapi.flow.runtime.impl.ExecutorSynapseFactory;
import com.hammurapi.reasoning.ExceptionHandler;
import com.hammurapi.util.concurrent.TaskCounter;

/**
 * 
 * @author Pavel Vlasov
 *
 * @param <S> Flow state type
 * @param <A> Argument type
 */
public class PriorityExecutorSynapseFactory<S,A> extends ExecutorSynapseFactory<Integer, S, A> {
	
	@Override
	public Destroyable connect(
			Invoker<S,A> invoker, 
			Invocable<S,A> invocable,
			com.hammurapi.flow.runtime.SynapseFactory.SynapseConfig config,
			Integer connectKey, 
			ProcessingPathElement processingPathElement,
			TaskCounter taskCounter) throws ConfigurationException {
		
		if (connectKey==null) {
			throw new NullPointerException("Connect key (priority) must not be null."); 
		}
		return super.connect(invoker, invocable, config, connectKey, processingPathElement, taskCounter);
	}
	
	@Override
	protected FutureTask<List<Future<?>>> createTask(
			final Callable<List<Future<?>>> callable, 
			TaskCounter taskCounter,
			Integer connectKey, 
			S flowState,
			A[] args) throws Exception {
		
		Callable<List<Future<?>>> exceptionHandlingCallable = new Callable<List<Future<?>>>() {

			@Override
			public List<Future<?>> call() throws Exception {
				if (exceptionHandler==null) {
					return callable.call();
				}
				try {
					return callable.call();
				} catch (Exception e) {
					exceptionHandler.handleException(e);
					return Collections.emptyList(); // If exception is handled.
				}
			}
		};
		
		return new NonBlockingPriorityNotifyingFutureTask<List<Future<?>>, A>(exceptionHandlingCallable, taskCounter, args, connectKey);
	}
	
	private ExceptionHandler exceptionHandler;
	
	public void setExceptionHandler(ExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
	}
	
	public ExceptionHandler getExceptionHandler() {
		return exceptionHandler;
	}

}
