package jp.hasc.hasctool.core.blockdiagram;

import java.util.HashMap;
import java.util.Map;

import jp.hasc.hasctool.core.blockdiagram.model.AbstractBlock;
import jp.hasc.hasctool.core.blockdiagram.model.BlockDiagram;
import jp.hasc.hasctool.core.blockdiagram.model.Connection;
import jp.hasc.hasctool.core.blockdiagram.model.PortReference;
import jp.hasc.hasctool.core.messaging.MessageConnector;
import jp.hasc.hasctool.core.messaging.MessageProcessor;
import jp.hasc.hasctool.core.runtime.RuntimeBean;
import jp.hasc.hasctool.core.runtime.RuntimeContext;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * BlockDiagramを実行する
 * @author iwasaki
 */
public class BlockDiagramExecutor {
	private RuntimeContext runtimeContext_;
	
	private BlockDiagram blockDiagram_;
	
	private Map<String, AbstractBlock> blocks_;

	private Map<String, Object> blockInstances_;

	public static final String KEY_BLOCK_DIAGRAM_FILE_NAME = "blockDiagramFileName"; //$NON-NLS-1$
	
	public BlockDiagramExecutor() {
		runtimeContext_=new RuntimeContext();
	}
	
	public BlockDiagramExecutor(RuntimeContext runtimeContext) {
		runtimeContext_=runtimeContext;
	}
	
	public RuntimeContext getRuntimeContext() {
		return runtimeContext_;
	}
	
	public void execute(BlockDiagram bd) {
		doLoad(bd);
		doCreateInstances();
		doSetup();
		doConnect();
		doStart();
	}

	public void doLoad(BlockDiagram bd) {
		blockDiagram_=bd;
		
		blocks_=new HashMap<String, AbstractBlock>();
		blockInstances_=new HashMap<String, Object>();
		
		// read blocks
		for(AbstractBlock b : blockDiagram_.getBlocks()) {
			//if (!b.isEnabled()) continue;
			blocks_.put(b.getName(), b); // 辞書には入れておく
		}
	}
	
	public void doCreateInstances() {
		// newRuntimeInstance
		for(AbstractBlock b : blockDiagram_.getBlocks()) {
			if (!b.isEnabled()) continue;
			blockInstances_.put(b.getName(), b.newRuntimeInstance(this));
		}
	}
		
	public void doSetup() {
		// setup
		for(AbstractBlock b : blockDiagram_.getBlocks()) {
			if (!b.isEnabled()) continue;
			Object bean=blockInstances_.get(b.getName());
			if (bean instanceof RuntimeBean) {
				RuntimeBean rb = (RuntimeBean) bean;
				rb.setup(getRuntimeContext());
			}
		}
	}
	
	public void doConnect() {
		// apply connections
		for (Connection c : blockDiagram_.getConnections()) {
			MessageConnector op=(MessageConnector) getPort(c.getOutputPortReference());
			MessageProcessor ip=(MessageProcessor) getPort(c.getInputPortReference());
			if (op!=null & ip!=null) op.connect(ip);
		}
	}
	
	public void doStart() {
		// run
		runtimeContext_.startTasks();
	}
	
	public AbstractBlock getBlock(String name) {
		return blocks_.get(name);
	}

	public Object getPort(PortReference pr) {
		AbstractBlock b = blocks_.get(pr.getBlockName());
		if (b==null) throw new RuntimeException("block '"+pr.getBlockName()+"' not found");
		if (!b.isEnabled()) return null;
		Object bi = blockInstances_.get(pr.getBlockName());
		return b.getRuntimePortByName(bi, pr.getPortName());
	}

	public static XStream newXStream() {
		XStream xs = new XStream(new DomDriver());
		xs.autodetectAnnotations(true);
		xs.aliasPackage("model", BlockDiagram.class.getPackage().getName()); //$NON-NLS-1$
		return xs;
	}

	public Object getBlockInsance(String blockName) {
		return blockInstances_.get(blockName);
	}

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