/**
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.common.fsm;

import java.util.ArrayList;

/**
 * 有限状態遷移機械
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public abstract class FiniteStateMachine {
	private final String name;

	/** 終了状態のリスト */
	private ArrayList finiteStates = new ArrayList();
	/** 状態が変化した際に呼び出されるリスナー */
	private ChangeStateListener changeStateListener = null;
	/** 状態 */
	private State state;

	/**
	 * デフォルトコンストラクタは使用禁止
	 */
	private FiniteStateMachine() {
		name = "no-name";
	}

	/**
	 * 状態遷移機械を作成するコンストラクタ
	 * @param name 状態遷移機械の名称
	 */
	public FiniteStateMachine(String name) throws Exception {
		this.name = name;
		init();
	}
	/**
	 * 初期化する
	 */
	public void init() throws Exception {
	}
	/**
	 * 終了処理
	 * @throws Exception
	 */
	public void finish() throws Exception {
	}
	/**
	 * 状態遷移機械に対して，終了状態を追加する
	 * 状態遷移機械は，状態が終了状態になった際に停止する
	 * @param state 状態
	 */
	public void addFiniteState(State state) {
		finiteStates.add(state);
	}
	/**
	 * ステップ実行をする
	 * @param symbol 入力記号
	 * @return true:継続中 / false:終了
	 */
	public boolean stepExec(Symbol symbol) {
		// イベントハンドラが登録されていれば，入力前にイベントの発生
		if(changeStateListener != null) {
			StateEvent e = new StateEvent(this, state);
			changeStateListener.onPreChanged(e);
		}
		input(symbol);
		// イベントハンドラが登録されていれば，入力後にイベントの発生
		if(changeStateListener != null) {
			StateEvent e = new StateEvent(this, state);
			changeStateListener.onPostChanged(e);
		}

		return isFiniteState() == false;
	}

	/**
	 * 終了状態になるまで継続して実行する
	 * @param 入力記号列
	 */
	public void execute(Symbol[] symbols) throws Exception {
		int cnt = 0;
		while(cnt < symbols.length && stepExec(symbols[cnt++]) == true);
		finish();
	}
	/**
	 * 状態が変化した際に呼び出されるイベントリスナーを登録する
	 * @param listener 状態遷移時のイベントリスナー
	 */
	public void setStateChangedListener(ChangeStateListener listener) {
		// 現状のものを削除してから設定する
		removeStateChangedLister();
		changeStateListener = listener;
	}
	/**
	 * 状態変化時のイベントリスナーを除去する
	 */
	public void removeStateChangedLister() {
		changeStateListener = null;
	}


	//--------------------- これ以降はサブクラスで実装すること -------------------
	/**
	 * 入力記号を状態に対して与える
	 * 入力された記号によって，状態が遷移したとされるタイミングでイベントが発生する
	 * @param symbol 入力記号
	 */
	protected abstract void input(Symbol symbol);
	/**
	 * 現在の状態が終了状態にあるかどうかを返す
	 * @return true:終了状態 / false:継続状態
	 */
	protected abstract boolean isFiniteState();
	/**
	 * 結果を返す
	 * @return 結果
	 */
	public abstract Object getResult();
}
