package jp.hasc.hasctool.core.runtime.hierarchize;

import java.io.InputStreamReader;
import java.util.Map;

import jp.hasc.hasctool.core.blockdiagram.BlockDiagramExecutor;
import jp.hasc.hasctool.core.blockdiagram.model.BlockDiagram;
import jp.hasc.hasctool.core.runtime.FileStreamProvider;
import jp.hasc.hasctool.core.runtime.RuntimeContext;
import jp.hasc.hasctool.core.util.CoreUtil;
import jp.hasc.hasctool.ui.commands.BlockDiagramExecutorManager;
import jp.hasc.hasctool.ui.commands.HybridStreamProvider;
import jp.hasc.hasctool.ui.commands.ResourceStreamProvider;

import org.apache.commons.beanutils.PropertyUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.ui.IWorkbenchWindow;

import com.thoughtworks.xstream.XStream;

/**
 * 内部でxbdファイルを読み込んで実行するためのヘルパーです。
 * @author iwasaki
 */
public class InnerXbdExecutor {
	/** logger for this class */
	private final static org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
			.getLog(InnerXbdExecutor.class);
	
	private RuntimeContext outerContext_;
	private BlockDiagramExecutor innerExecutor_;
	//private Runnable stopTasksListener_;
	
	public void load(RuntimeContext outerContext, XbdExecutionRequest req) {
		try{
			outerContext_=outerContext;
			String xbdFilePath=req.getXbdFilePath();
			//
			FileStreamProvider fsp = outerContext.getFileStreamProvider();
			
			// setup context
			innerExecutor_=new BlockDiagramExecutor(new RuntimeContext(outerContext_));
			final RuntimeContext innerContext = innerExecutor_.getRuntimeContext();
			innerContext.putObjectToRepogitory(IWorkbenchWindow.class, outerContext.getObjectFromRepogitory(IWorkbenchWindow.class));
			if (fsp instanceof ResourceStreamProvider) {
				ResourceStreamProvider rsp=(ResourceStreamProvider) fsp;
				IFile innerXbdFile=rsp.getFile(xbdFilePath);
				innerContext.putObjectToRepogitory(BlockDiagramExecutor.KEY_BLOCK_DIAGRAM_FILE_NAME, innerXbdFile.getName());
				innerContext.setFileStreamProvider(new HybridStreamProvider(innerXbdFile.getParent(), innerContext));
			}else{
				innerContext.setFileStreamProvider(fsp);
			}
			
			// load xbd
			String contents=CoreUtil.readReaderAsString(new InputStreamReader(
					fsp.openInputStream(xbdFilePath),RuntimeContext.DEFAULT_CHARSET));
			XStream xs = BlockDiagramExecutor.newXStream();
			BlockDiagram bd = (BlockDiagram)xs.fromXML(preprocessXbd(contents, req));
			innerExecutor_.doLoad(bd);
			innerExecutor_.doCreateInstances();
			
			modifyProperties(req);
			
			// setup
			innerExecutor_.doSetup();
			innerExecutor_.doConnect();
		}catch(Exception ex) {
			CoreUtil.throwAsRuntimeException(ex);
		}
	}
	
	private void modifyProperties(XbdExecutionRequest req) {
		for(Map.Entry<String,Object> e: req.getProperties().entrySet()) {
			String key=e.getKey();
			int idx=key.indexOf('.');
			if (idx<0) {
				LOG.warn("invalid property key: "+key);
				continue;
			}
			String blockName=key.substring(0,idx);
			String propName=key.substring(idx+1);
			Object bean=innerExecutor_.getBlockInsance(blockName);
			if (bean==null) {
				LOG.warn("block not found: "+blockName);
				continue;
			}
			try {
				PropertyUtils.setProperty(bean, propName, e.getValue());
			} catch (Exception ex) {
				LOG.warn("Exception",ex);
			}
		}
	}

	private static String preprocessXbd(String contents, XbdExecutionRequest req) {
		for(Map.Entry<String,String> e: req.getReplacingsInXbd().entrySet()) {
			if (contents.contains(e.getKey())) {
				contents=contents.replace(e.getKey(), e.getValue());
			}
		}
		contents=BlockDiagramExecutorManager.preprocess_currentDateTime(contents);
		return contents;
	}

	public void start() {
		/*
		if (stopTasksListener_!=null) throw new IllegalStateException();
		stopTasksListener_ = new Runnable() {
			@Override
			public void run() {
				innerExecutor_.getRuntimeContext().stopTasks();
			}
		};
		*/
		outerContext_.addChildContext(innerExecutor_.getRuntimeContext());
		//outerContext_.addStopTasksListener(stopTasksListener_);
		innerExecutor_.doStart();
		getInnerContext().addTasksTerminationListener(new Runnable() {
			public void run() {
				outerContext_.removeChildContext(innerExecutor_.getRuntimeContext());
				//outerContext_.removeStopTasksListener(stopTasksListener_);
				//stopTasksListener_=null;
			}
		});
	}
	
	public void awaitTasksTermination() throws InterruptedException {
		getInnerContext().awaitTasksTermination();
	}
	
	public BlockDiagramExecutor getInnerExecutor() {
		return innerExecutor_;
	}
	
	public RuntimeContext getInnerContext() {
		return innerExecutor_.getRuntimeContext();
	}

	public RuntimeContext getOuterContext() {
		return outerContext_;
	}

	/*
	public void dispose() {
		innerExecutor_.dispose();
	}
	*/
}
