/*******************************************************************************
 * Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0-standalone.html
 ******************************************************************************/
package se.unlogic.standardutils.threads;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import se.unlogic.standardutils.xml.Elementable;
import se.unlogic.standardutils.xml.XMLAttribute;
import se.unlogic.standardutils.xml.XMLElement;
import se.unlogic.standardutils.xml.XMLGenerator;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

@XMLElement(name="ExecutionController")
public class SimpleExecutionController<T extends TaskGroup> implements ExecutionController<T>, Elementable{

	private final ReentrantLock globalLock = new ReentrantLock();
	private Condition finishedCondition = globalLock.newCondition();

	private T taskGroup;
	
	private BlockingQueue<? extends Runnable> taskQueue;
	private ThreadPoolTaskGroupHandler<T> threadPoolTaskHandler;

	@XMLAttribute
	private boolean started;
	
	@XMLAttribute
	private boolean aborted;
	
	@XMLAttribute
	private int initialTaskCount;
	
	@XMLAttribute
	private AtomicInteger completedTaskCount = new AtomicInteger();
	
	public SimpleExecutionController(T taskGroup, ThreadPoolTaskGroupHandler<T> threadPoolTaskHandler) {

		super();
		this.taskGroup = taskGroup;
		this.taskQueue = taskGroup.getTasks();
		this.threadPoolTaskHandler = threadPoolTaskHandler;
		initialTaskCount = taskQueue.size();
	}	
	
	public void abort() {

		globalLock.lock();
		
		try{
			
			if(this.threadPoolTaskHandler != null){
				
				if(started){
					threadPoolTaskHandler.remove(this);
				}
				
				this.aborted = true;
				
				this.executionComplete();
			}
			
		}finally{
			
			globalLock.unlock();
		}
	}

	public void awaitExecution(long timeout) throws InterruptedException {

		globalLock.lock();
		
		try{
			
			if(finishedCondition != null){
				
				finishedCondition.await(timeout, TimeUnit.MILLISECONDS);
			}
			
		}finally{
			
			globalLock.unlock();
		}

	}

	public void awaitExecution() throws InterruptedException {

		globalLock.lock();
		
		try{
			
			if(finishedCondition != null){
				
				finishedCondition.await();
			}
			
		}finally{
			
			globalLock.unlock();
		}

	}	
	
	void executionComplete(){
				
		globalLock.lock();
		
		try{
			
			if(finishedCondition != null){
				
				finishedCondition.signalAll();
				
				threadPoolTaskHandler = null;
				finishedCondition = null;
			}			
						
		}finally{
			
			globalLock.unlock();
		}		
	}
	
	public int getRemainingTaskCount() {

		return taskQueue.size();
	}

	
	BlockingQueue<? extends Runnable> getTaskQueue() {
	
		return taskQueue;
	}

	public void start() {

		globalLock.lock();
		
		try{
			if(!started && !aborted){
				
				this.threadPoolTaskHandler.add(this);
				this.started = true;
			}
			
		}finally{
			
			globalLock.unlock();
		}
	}

	
	public int getInitialTaskCount() {
	
		return initialTaskCount;
	}

	
	public int getCompletedTaskCount() {
	
		return completedTaskCount.get();
	}
	
	void incrementCompletedTaskCount(){
		
		this.completedTaskCount.incrementAndGet();
	}
	
	public boolean isStarted(){
		
		return started;
	}
	
	public boolean isAborted(){
		
		return aborted;
	}
	
	public boolean isFinished(){
		
		return started && !aborted && this.threadPoolTaskHandler == null;
	}

	
	public T getTaskGroup() {
	
		return taskGroup;
	}

	public Element toXML(Document doc) {

		return XMLGenerator.toXML(this, doc);
	}
}
