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

import jp.hasc.hasctool.core.blockdiagram.BlockDiagramExecutor;
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.AbstractTask;
import jp.hasc.hasctool.core.runtime.RuntimeContext;

/**
 * プロパティで指定されたxbdファイルを読み込んで、内部で実行するブロックです。
 * 外側と、inputPort/outputPortを接続可能です（１入力１出力まで）。
 * 読み込むxbdファイルのパスをxbdFilePathプロパティで指定して下さい。
 * 
 * 内側（読み込まれたブロック図の中）のポートと、外側（このブロック自体）のポートは、デフォルトでは以下のように対応します。
 * <pre>
 * * 外側の inputPort = 内側の"input"という名前のブロックの、inputPort
 * * 外側の outputPort = 内側の"output"という名前のブロックの、outputPort
 * </pre>
 * この対応は、innerInputPortName、innerOutputPortNameプロパティで変えられます（形式：blockName.portName）。
 * 外部と接続するためだけのブロック（変換処理を行わないフィルタ）が必要な場合は、
 * {@link jp.hasc.hasctool.core.runtime.hierarchize.IdentityFilter} を使うことができます。
 * 
 * @author iwasaki
 */
public class XbdBlock extends AbstractTask {
	private String innerInputPortName_ = null;
	private String innerOutputPortName_ = null;
	private String xbdFilePath_ = null;
	private XbdExecutionRequest xbdExecutionRequest_ = null;
	private InnerXbdExecutor xbdExecutor_=new InnerXbdExecutor();

	@Override
	public void setup(RuntimeContext outerContext) {
		super.setup(outerContext);
		if (xbdExecutionRequest_==null) {
			xbdExecutionRequest_=new XbdExecutionRequest();
			xbdExecutionRequest_.setXbdFilePath(xbdFilePath_);
		}else{
			if (xbdFilePath_!=null) {
				xbdExecutionRequest_.setXbdFilePath(xbdFilePath_);
			}
		}
		xbdExecutor_.load(outerContext, xbdExecutionRequest_);
	}

	@Override
	protected void run() throws InterruptedException {
		xbdExecutor_.start();
		xbdExecutor_.awaitTasksTermination();
		//xbdExecutor_.dispose();
	}

	public static PortReference createPortReferenceFromString(String portProp) {
		int pos=portProp.indexOf('.');
		if (pos<0) throw new IllegalArgumentException();
		String blockName=portProp.substring(0,pos);
		String portName=portProp.substring(pos+1);
		return new PortReference(blockName, portName);
	}
	
	private BlockDiagramExecutor getInnerExecutor() {
		return xbdExecutor_.getInnerExecutor();
	}
	
	// ports
	public MessageProcessor getInputPort() {
		if (innerInputPortName_!=null) {
			return (MessageProcessor) getInnerExecutor().getPort(createPortReferenceFromString(innerInputPortName_));
		}
		return (MessageProcessor) getInnerExecutor().getPort(new PortReference("input","inputPort"));
	}

	public MessageConnector getOutputPort() {
		if (innerOutputPortName_!=null) {
			return (MessageConnector) getInnerExecutor().getPort(createPortReferenceFromString(innerOutputPortName_));
		}
		return (MessageConnector) getInnerExecutor().getPort(new PortReference("output","outputPort"));
	}

	// props
	
	/**
	 * 実行するxbdファイルのパスを指定します
	 */
	public void setXbdFilePath(String xbdFilePath) {
		xbdFilePath_ = xbdFilePath;
	}
	
	/**
	 * 外側のinputPortに対応する、内側（読み込まれたブロック図内）のブロックとポートを指定します。形式：blockName.portName
	 */
	public void setInnerInputPortName(String innerInputPort) {
		innerInputPortName_ = innerInputPort;
	}
	
	/**
	 * 外側のoutputPortに対応する、内側（読み込まれたブロック図内）のブロックとポートを指定します。形式：blockName.portName
	 */
	public void setInnerOutputPortName(String innerOutputPort) {
		innerOutputPortName_ = innerOutputPort;
	}

	/**
	 * 実行するXbdExecutionRequestオブジェクトを指定します
	 */
	public void setXbdExecutionRequest(XbdExecutionRequest xbdExecutionRequest) {
		xbdExecutionRequest_ = xbdExecutionRequest;
	}

}
