/* $Id: SimurateManager.java,v 1.2 2007/12/11 09:54:58 nito Exp $
 * 
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow.
 * 
 *  Nautica Workflow is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Nautica Workflow; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.tool.wfd.sim;

import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Timer;

import jp.co.argo21.nautica.tool.wfd.element.ActivityElement;
import jp.co.argo21.nautica.tool.wfd.element.ProcessElement;
import jp.co.argo21.nautica.tool.wfd.element.TransitionElement;
import jp.co.argo21.nautica.tool.wfd.expression.ConditionalVariable;
import jp.co.argo21.nautica.tool.wfd.expression.Expression;

/**
 * フローシミュレーションを制御するマネージャ。
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.2 $
 * @since   Nautica Workflow 1.0
 */
public class SimurateManager
{
	/** シリアルバージョンID */
	private static final long serialVersionUID = 1L;

	/** シミュレート再描画までの待ち時間 */
	private static final int TIMER_DELAY = 1000; /* (ms) */

	/** シミュレーションが進展したことを表すフラグ */
	private static final int SIMULATION_PROGRESSED = 1;
	/** シミュレーションが完了したことを表すフラグ */
	private static final int SIMULATION_FINISHED   = 2;
	/** シミュレーションが途中で中断したことを表すフラグ */
	private static final int SIMULATION_TERMINATED = 3;
	
	/** シミュレーション用の変数設定ダイアログ */
	private SimulatorDialog dialog;
	/** シミュレート状況を受け取るリスナー */
	private SimulationListener listener;
	/** シミュレート対象のプロセス要素 */
	private ProcessElement proc;
	/** シミュレート対象の擬似アクティビティインスタンスラッパー */
	private SimulatableActivityWrapper wrapper;

	/**
	 * シミュレータを生成する。
	 */
	public SimurateManager()
	{
		dialog = new SimulatorDialog();
	}
	
	/**
	 * シミュレートを開始する。
	 * 
	 * @param l	リスナー
	 * @param vars	プロセス変数
	 * @param proc	対象プロセス
	 * @param act	開始アクティビティラッパー
	 */
	public void start(
	SimulationListener l,
	List<ConditionalVariable> vars,
	ProcessElement proc,
	SimulatableActivityWrapper act)
	{
		this.listener = l;
		this.proc = proc;
		this.wrapper = act;
		dialog.setCurrentActivity(act.getActivity());
		dialog.setCurrentVariables(vars);

		if (listener != null) listener.simulationStarted(act);
		
		simulate();
	}

	/**
	 * １ステップ分シミュレートする。
	 * 
	 * @param act
	 */	
	public void simulate(SimulatableActivityWrapper act)
	{
		this.wrapper = act;
		dialog.setCurrentActivity(act.getActivity());
		
		simulate();
	}

	/**
	 * シミュレート実処理。
	 */
	private void simulate()
	{
		if (wrapper.getActivity().getType().equals(ActivityElement.CONCUR_START)
			|| wrapper.getActivity().getType().equals(ActivityElement.CONCUR_END)
		|| wrapper.getActivity().getType().equals(ActivityElement.END)) {
			dialog.setValueEditable(false);
		} else {
			dialog.setValueEditable(true);
		}

		dialog.setVisible(true);

		if (dialog.getResult() == SimulatorDialog.EVAL_OPTION) {
			SimulateAction action = null;
			if (wrapper.getActivity().getType().equals(ActivityElement.END)) {
				action = new SimulateAction(SIMULATION_FINISHED);
			} else {
				List<ConditionalVariable> vars = dialog.getCurrentVariables();
				boolean isExRaising = dialog.isExceptionRaising();
				List<TransitionElement> trs = evaluate(vars, isExRaising);

				if (trs.size() == 0) {
					action = new SimulateAction(SIMULATION_TERMINATED);
					action.setSimulatedActivity(wrapper);
				} else {
					action = new SimulateAction(SIMULATION_PROGRESSED);
					action.setSimulatedTransitions(trs);
				}
			}

			Timer timer = new Timer(TIMER_DELAY, action);
			timer.setRepeats(false);
			timer.start();
		}
	}

	/**
	 * 条件の判定を行う。
	 * 
	 * @param vars	プロセス変数
	 * @param isExRaising	例外発生
	 * @return	シミュレート結果選択された遷移
	 */
	private List<TransitionElement> evaluate(List<ConditionalVariable> vars, boolean isExRaising)
	{
		List<TransitionElement> result = new ArrayList<TransitionElement>();
		List<TransitionElement> otherwises = new ArrayList<TransitionElement>();
		boolean isConcurrent = false;
		if (wrapper.getActivity().getType().equals(ActivityElement.CONCUR_START)) {
			isConcurrent = true;
		}

		for (TransitionElement tr : proc.getTransitions()) {
			if (wrapper.getActivity() != tr.getSource()) continue;

			Expression exp = tr.getCondition();
			int type = tr.getType();
			
			if (type == TransitionElement.EXCEPTION) {
				if (isExRaising) {
					result.add(tr);
					if (isConcurrent == false) break;
				}
			} else if (type == TransitionElement.OTHERWISE) {
				otherwises.add(tr);
			} else {
				if (exp == null) {
					//条件式がない場合は無条件遷移とみなす。
					result.add(tr);
					if (isConcurrent == false) break;
				} else {
					if (exp.eval(vars)) {
						result.add(tr);
						if (isConcurrent == false) break;
					}
				}
			}
		}

		//代替遷移の判定
		if (result.size() == 0) {
			if (otherwises.size() > 0) {
				if (isConcurrent) {
					result = otherwises;
				} else {
					result.add(otherwises.get(0));
				}
			}
		}

		return result;
	}
	
	/**
	 * シミュレータアニメーションを実現するためのタイマーアクション
	 */	
	class SimulateAction extends AbstractAction
	{
		/** シリアルバージョンID */
		private static final long serialVersionUID = 1L;

		/** シミュレート状態 */
		private int state;
		/** 対象アクティビティラッパー */
		private SimulatableActivityWrapper simact;
		/** 結果遷移 */
		private List<TransitionElement> simtrs;

		/**
		 * アクションを生成する。
		 * 
		 * @param s	状態
		 */		
		SimulateAction(int s)
		{
			this.state = s;
		}

		/**
		 * 状態に応じたアクション実行。
		 * 
		 * @param e	アクションイベント
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */		
		public void actionPerformed(ActionEvent e)
		{
			if (state == SIMULATION_PROGRESSED) {
				fireSimulationProgressed();
			} else if (state == SIMULATION_FINISHED) {
				fireSimulationFinished();
			} else if (state == SIMULATION_TERMINATED) {
				fireSimulationTerminated();
			}
		}

		/**
		 * シミュレート対象のアクティビティラッパーを設定する。
		 * 
		 * @param a	アクティビティラッパー
		 */		
		void setSimulatedActivity(SimulatableActivityWrapper a)
		{
			this.simact = a;
		}
	
		/**
		 * 結果遷移を設定する。
		 * 
		 * @param trs	結果遷移
		 */		
		void setSimulatedTransitions(List<TransitionElement> trs)
		{
			this.simtrs = trs;
		}

		/**
		 * シミュレートの進展を通知する。
		 */
		private void fireSimulationProgressed()
		{
			if (listener != null) listener.simulationProgressed(wrapper, simtrs);
		}

		/**
		 * シミュレートの完了を通知する。
		 */
		private void fireSimulationFinished()
		{
			if (listener != null) listener.simulationFinished(simact);
		}

		/**
		 * シミュレートの中断を通知する。
		 */
		private void fireSimulationTerminated()
		{
			if (listener != null) listener.simulationTerminated(simact);
		}
	}
}
