package j3deditor.bin.util;

import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Executes all tasks added to its task queue sequentially
 * in a low priority "daemon" thread.
 *
 * @author Risto Seene
 *
 * @see BackgroundTask
 */
public class BackgroundThread extends Thread{

	/** the BackgroundThread greated during the execution of the application. */
	private static final BackgroundThread THREAD = new BackgroundThread();

	/** task queue. */
	private ConcurrentLinkedQueue<BackgroundTask> taskQueue;

	/** synchronizer. */
	private Object lock = new Object();


	/**
	 * Creates an instance of <code>BackgroundThread</code>.
	 */
	protected BackgroundThread(){
		this.setName("Background Thread");
		this.setPriority(Thread.MIN_PRIORITY);
		this.setDaemon(true);

		this.taskQueue = new ConcurrentLinkedQueue<BackgroundTask>();

		try{
			this.start();
		} catch(Throwable t){
			Utilities.out.throwFatalError(t);
		}
	}


	/**
	 * Adds the specified task to the task queue
	 * and executes it as soon as possible.
	 * 
	 * @param task
	 * 	task to be executed
	 */
	public static void execute(BackgroundTask task){
		THREAD.addTask(task);
	}


	/**
	 * Adds the specified task to the task queue
	 * only if equivalent task can not be found in the queue,
	 * and executes it as soon as possible.
	 * 
	 * @param task
	 * 	task to be executed
	 */
	public static void executeOnce(BackgroundTask task){
		THREAD.addUniqueTask(task);
	}
	
	
	/**
	 * Adds the specified task to the task queue.
	 * 
	 * @param task
	 * 	task to be added to the queue
	 */
	private synchronized void addTask(BackgroundTask task){
		synchronized(lock){
			this.taskQueue.offer(task);
			lock.notifyAll();
		}
	}
	
	
	/**
	 * Adds the specified task to the task queue
	 * only if equivalent task can not be found in the queue.
	 * 
	 * @param task
	 * 	task to be added to the queue
	 */
	private synchronized void addUniqueTask(BackgroundTask task){
		synchronized(lock){
			if(!this.taskQueue.contains(task)){
				this.taskQueue.offer(task);
				lock.notifyAll();
			}
		}
	}


	/**
	 * Returns next task from the task queue.
	 * <br/>
	 * If the queue is empty, waits until new task is added.
	 * 
	 * @return next task from the queue
	 */
	private BackgroundTask getTask(){
		synchronized(lock){
			BackgroundTask task = this.taskQueue.poll();
			if(task == null){
				try{
					lock.wait();
					task = this.taskQueue.poll();
				} catch(Throwable t){}
			}
			
			return task;
		}
	}


	/**
	 * Executes sequentially all tasks in the task queue.
	 * Not to be called directly by any application.
	 */
	@Override
	public void run(){
		while(true){
			try{
				getTask().process();
			} catch(Throwable t){
				Utilities.out.throwError(t);
			}
		}
	}
	
}