/**
 * 
 */
package com.trendrr.oss.taskprocessor;

import java.util.Comparator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.trendrr.oss.FileCache;
import com.trendrr.oss.PriorityUpdateQueue;
import com.trendrr.oss.concurrent.LazyInitObject;
import com.trendrr.oss.executionreport.ExecutionReport;
import com.trendrr.oss.executionreport.ExecutionReportIncrementor;
import com.trendrr.oss.executionreport.ExecutionSubReport;
import com.trendrr.oss.taskprocessor.Task.ASYNCH;



/**
 * @author Dustin Norlander
 * @created Sep 24, 2012
 * 
 */
public class TaskProcessor {
	protected static Log log = LogFactory.getLog(TaskProcessor.class);
	
	
	static class TaskProcessorThreadFactory implements ThreadFactory {
        static final AtomicInteger poolNumber = new AtomicInteger(1);
        final ThreadGroup group;
        final AtomicInteger threadNumber = new AtomicInteger(1);
        final String namePrefix;

        TaskProcessorThreadFactory(String name) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null)? s.getThreadGroup() :
                                 Thread.currentThread().getThreadGroup();
            namePrefix = "TP-" + name +"-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(true);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
	
	static LazyInitObject<AsynchTaskRegistery> asynchTasks = new LazyInitObject<AsynchTaskRegistery>() {
		@Override
		public AsynchTaskRegistery init() {
			AsynchTaskRegistery reg = new AsynchTaskRegistery();
			reg.start();
			return reg;
		}
	};
	
	protected ExecutorService threadPool = null;
	protected String name;
	protected TaskCallback callback;
	protected ExecutionReportIncrementor exreport;
	
	//example threadpool, blocks when queue is full. 
//	ExecutorService threadPool = new ThreadPoolExecutor(
//			1, // core size
//		    30, // max size
//		    130, // idle timeout
//		    TimeUnit.SECONDS,
//		    new ArrayBlockingQueue<Runnable>(30), // queue with a size
//		    new ThreadPoolExecutor.CallerRunsPolicy() //if queue is full run in current thread.
//			);
	
	
	/**
	 * creates a new TaskProcessor with a new executorService 
	 * ExecutorService threadPool = new ThreadPoolExecutor(
			1, // core size
		    numThreads, // max size
		    130, // idle timeout
		    TimeUnit.SECONDS,
		    new ArrayBlockingQueue<Runnable>(numThreads), // queue with a size
		    new ThreadPoolExecutor.CallerRunsPolicy() //if queue is full run in current thread.
			);
	 * 
	 * @param name
	 * @param callback
	 * @param numThreads
	 */
	public static TaskProcessor defaultInstance(String name, TaskCallback callback, int numThreads) {
		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
				1, // core size
			    numThreads, // max size
			    130, // idle timeout
			    TimeUnit.SECONDS,
			    new ArrayBlockingQueue<Runnable>(numThreads), // queue with a size
			    new TaskProcessorThreadFactory(name),
			    new ThreadPoolExecutor.CallerRunsPolicy() //if queue is full run in current thread.
				);
		
		return new TaskProcessor(name, callback, threadPool);
	}
	
	/**
	 * creates a new task processor.  
	 * 
	 * 
	 * 
	 * 
	 * @param name The name of this processor. used for execution reporting.
	 * @param callback Methods called for every task execution.  this callback is called before the callback specified in the task. can be null. 
	 * @param executor the executor
	 */
	public TaskProcessor(String name, TaskCallback callback, ExecutorService executor) {
		this.threadPool = executor;
		this.name = name;
		this.callback = callback;
		this.exreport = new ExecutionSubReport(this.getName(), ExecutionReport.instance("TaskProcessor"));
	}
	
	
	/**
	 * submits a task for execution.
	 * @param task
	 */
	public void submitTask(Task task) {
		if (task.getSubmitted() == null) {
			task.submitted();
		}
		task.setProcessor(this);
		//add to the executor service.. 
		TaskFilterRunner runner = new TaskFilterRunner(task);
		this.threadPool.execute(runner);
	}
	
	public void setAsynch(Task t, ASYNCH asynch, long timeout) {
		asynchTasks.get().add(t, asynch, timeout);
	}
	
	/**
	 * submit a future, a separate thread will poll it on interval and 
	 * call your callback when isDone is set, or cancel once the timeout has.
	 * 
	 * callback is executed in one of this processors executor threads.
	 * @param future
	 * @param callback
	 */
	public void submitFuture(Task task, Future future, FuturePollerCallback callback, long timeout) {
		FuturePollerWrapper wrapper = new FuturePollerWrapper(future, callback, timeout, task);
		asynchTasks.get().addFuture(wrapper);
	}
	
	public void resumeAsynch(String taskId) {
		asynchTasks.get().resume(taskId);
	}
	
	public ExecutorService getExecutor() {
		return this.threadPool;
	}
	/**
	 * A unique name for this processor.  only one instance per name will be allowed.
	 * @return
	 */
	public String getName() {
		return this.name;
	}
	
	public void taskComplete(Task task) {
		if (this.callback != null) {
			this.callback.taskComplete(task);
		}
		if (task.getCallback() != null) {
			task.getCallback().taskComplete(task);
		}
	}
	
	public void taskError(Task task, Exception error) {
		if (this.callback != null) {
			this.callback.taskError(task, error);
		}
		if (task.getCallback() != null) {
			task.getCallback().taskError(task, error);
		}
	}
	
	/**
	 * gets the execution report incrementor for TaskProcessor.{this.getName}
	 * 
	 * @return
	 */
	public ExecutionReportIncrementor getExecutionReport() {
		return exreport;
	}
}

@@ -2483,58 +2483,10 @@
 ck;%0A
-%09protected ExecutionReportIncrementor exreport;%0A
 %09%0A
+
 %09//e
@@ -4344,109 +4344,8 @@
 ck;%0A
-%09%09this.exreport = new ExecutionSubReport(this.getName(), ExecutionReport.instance(%22TaskProcessor%22));%0A
 %09%7D%0A%09
@@ -6177,16 +6177,89 @@
 urn 
-exreport
+new ExecutionSubReport(this.getName(), ExecutionReport.instance(%22TaskProcessor%22))
 ;%0A%09%7D

create the ex report on demand