package jp.hasc.hasctool.core.runtime;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

import jp.hasc.hasctool.core.messaging.MessageQueue;

/**
 * タスク実行時に渡されるオブジェクト群です
 * @author iwasaki
 */
public class RuntimeContext {
	/** logger for this class */
	private final static org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
			.getLog(RuntimeContext.class);
	
	private TaskManager taskManager_;
	
	private RuntimeContext parentContext_=null;
	private ArrayList<RuntimeContext> childContexts_=new ArrayList<RuntimeContext>();
	
	private void doInit() {
		taskManager_=new TaskManager(this);
	}
	
	public RuntimeContext() {
		doInit();
	}
	
	public RuntimeContext(RuntimeContext parentContext) {
		parentContext_=parentContext;
		doInit();
	}
	
	public void addChildContext(RuntimeContext c) {
		childContexts_.add(c);
	}
	
	public void removeChildContext(RuntimeContext c) {
		childContexts_.remove(c);
	}

	private int defaultMessageQueueCapacity_ = Integer.MAX_VALUE;
	private ArrayList<MessageQueue> messageQueues_ = new ArrayList<MessageQueue>();
	private FileStreamProvider fileStreamProvider_;
	private Map<String, Object> repogitory_=new HashMap<String, Object>();
	private ArrayList<Runnable> stopTasksListeners_=new ArrayList<Runnable>();

	public static final Charset DEFAULT_CHARSET=Charset.forName("UTF-8");

	TaskManager getTaskManager() {
		return taskManager_;
	}
	
	public MessageQueue createMessageQueue(int capacity) {
		MessageQueue queue=(capacity==Integer.MAX_VALUE) ? new MessageQueue() : new MessageQueue(capacity);
		synchronized (messageQueues_) { 
			messageQueues_.add(queue);
		}
		return queue;
	}
	
	public MessageQueue createDefaultMessageQueue() {
		return createMessageQueue(defaultMessageQueueCapacity_);
	}

	public void setDefaultMessageQueueCapacity(int queueCapacity) {
		defaultMessageQueueCapacity_=queueCapacity;
	}

	public ArrayList<MessageQueue> getMessageQueues() {
		return messageQueues_;
	}

	public FileStreamProvider getFileStreamProvider() {
		return fileStreamProvider_;
	}

	public void setFileStreamProvider(FileStreamProvider fileStreamProvider) {
		fileStreamProvider_ = fileStreamProvider;
	}
	
	public void putObjectToRepogitory(String key,Object obj) {
		repogitory_.put(key, obj);
	}
	
	public void putObjectToRepogitory(Class<?> c, Object obj) {
		repogitory_.put(c.getName(), obj);
	}
	
	public Object getObjectFromRepogitory(String key) {
		return repogitory_.get(key);
	}
	
	public Object getObjectFromRepogitory(Class<?> c) {
		return repogitory_.get(c.getName());
	}
	
	public void startTasks() {
		taskManager_.startAll();
	}
	
	public void stopTasks() {
		synchronized (stopTasksListeners_) {
			for(Runnable r: stopTasksListeners_) {
				r.run();
			}
		}
		for(RuntimeContext childContext:childContexts_) {
			childContext.getTaskManager().shutdownPhase1();
		}
		taskManager_.shutdownPhase1();
		taskManager_.shutdownPhase2();
	}
	
	public void addStopTasksListener(Runnable r) {
		synchronized (stopTasksListeners_) {
			stopTasksListeners_.add(r);
		}
	}
	
	public void removeStopTasksListener(Runnable r) {
		synchronized (stopTasksListeners_) {
			stopTasksListeners_.remove(r);		
		}
	}
	
	public boolean awaitTasksTermination() throws InterruptedException {
		return awaitTasksTermination(0);
	}
	
	public boolean awaitTasksTermination(long timeout) throws InterruptedException {
		return taskManager_.awaitTermination(timeout);
	}
	
	/*
	public void dispose() {
		taskManager_.dispose();
	}
	*/
	
	public boolean isTasksRunning() {
		return taskManager_.isRunning();
	}
	
	private List<Runnable> tasksTerminationListeners_=Collections.synchronizedList(new ArrayList<Runnable>());

	void onTasksTerminated() {
		// clear queues
		ArrayList<MessageQueue> queues = getMessageQueues();
		synchronized (queues) { 
			for(MessageQueue queue:queues) {
				BlockingQueue<Object> bq = queue.getQueueImpl();
				if (!bq.isEmpty()) bq.clear();
			}
			queues.clear();
		}
		//
		ArrayList<Runnable> listeners=new ArrayList<Runnable>();
		listeners.addAll(tasksTerminationListeners_); // iterate中に変更されうるので、複製
		for(Runnable r:listeners) r.run();
	}

	public List<Runnable> tasksTerminationListeners() {
		return tasksTerminationListeners_;
	}
	
	public void addTasksTerminationListener(Runnable r) {
		tasksTerminationListeners_.add(r);
	}
	
	public void removeTasksTerminationListener(Runnable r) {
		tasksTerminationListeners_.remove(r);
	}
	
	private boolean stopTasksWhenUnhandledException_=true;

	public void onCatchUnhandledException(Throwable ex) {
		// terminate task
		if (taskManager_.isRunning() && stopTasksWhenUnhandledException_) {
			LOG.error("Stopping tasks due to the unhandled exception: "+ex.toString());
			stopTasks();
		}
	}

	public boolean isStopTasksWhenUnhandledException() {
		return stopTasksWhenUnhandledException_;
	}

	public void setStopTasksWhenUnhandledException(
			boolean stopTasksWhenUnhandledException) {
		stopTasksWhenUnhandledException_ = stopTasksWhenUnhandledException;
	}

	public RuntimeContext getParentContext() {
		return parentContext_;
	}

	public ArrayList<RuntimeContext> getChildContexts() {
		return childContexts_;
	}
	
}
