//
// CopyRight (C) 2002, IPA, eLC, NTT Resonant Inc. All rights reserved.
//
//	ActivityPath
//
//	変更履歴
//		2002.12.25  新規作成
//

package jp.co.ntt.lms.lo.scorm.kernel;

import jp.co.ntt.lms.lo.scorm.kernel.exception.SystemException;

/**
 * ActivityPathクラス<BR>
 * Activity関連情報を生成取得するクラス<BR>
 */
public class ActivityPath {

	/**
	 * activity path 相対関係 何れかのアクティビティがNULLの場合
	 */
	public static final int NULL_ACTIVITY = 0;

	/**
	 * activity path 相対関係 同じアクティビティの場合<BR>
	 */
	public static final int SAME_ACTIVITY = 1;

	/**
	 * activity path 相対関係 兄弟アクティビティの場合<BR>
	 */
	public static final int SIBLING = 2;

	/**
	 * activity path 相対関係 カレントアクティビティが共通の親の場合<BR>
	 */
	public static final int ACT1_ACTIVITY_IS_COMMON_ANCESTOR = 3;

	/**
	 * activity path 相対関係 ターゲットアクティビティが共通の親の場合<BR>
	 */
	public static final int ACT2_ACTIVITY_IS_COMMON_ANCESTOR = 4;

	/**
	 * activity path 相対関係 その他<BR>
	 */
	public static final int ETC = 5;

	/** activities in the activity path */
	private Activity[] activitySet;

	/** position */
	private int pos;

	/**
	 *	コンストラクタ<BR>
	 *
	 * @param	startActivity	アクティビティパスの始点アクティビティ<BR>
	 * @param   endActivity 	アクティビティパスの終点アクティビティ<BR>
	 */
	public ActivityPath(Activity startActivity, Activity endActivity) throws SystemException {

		try {
			pos = 0; // pos の初期化
			if (startActivity == null || endActivity == null) {
				activitySet = null;
				return;
			}

			//startActivityとendActivityが同じなら、そのアクティビティだけ入れる
			if (startActivity.getIdentifier().equals(endActivity.getIdentifier())) {
				activitySet = new Activity[1];
				activitySet[0] = startActivity;
			}
			else
				if ((!startActivity.isRoot()) && (!endActivity.isRoot())) {
					//兄弟のチェック(親が同じなら兄弟)
					if (startActivity.getParent() == endActivity.getParent()) { //兄弟なので横にpathを作る
						Activity[] actSet = startActivity.getParent().getAvailableChildren(); // 利用可能な兄弟Activityを取得
						int startOrder = 0;
						int endOrder = 0;
						int path_len = 0;
						boolean reverse_flg = false;

						if (startActivity.getAvailableOrder() - endActivity.getAvailableOrder() < 0) {
							startOrder = startActivity.getAvailableOrder();
							endOrder = endActivity.getAvailableOrder();
						}
						else {
							startOrder = endActivity.getAvailableOrder();
							endOrder = startActivity.getAvailableOrder();
							reverse_flg = true;
						}

						// パスの長さを調べる
						for (int i = 0; i < actSet.length; i++) {
							if (startOrder <= actSet[i].getAvailableOrder()
								&& endOrder >= actSet[i].getAvailableOrder()) {
								path_len++;
							}
						}

						// パスの初期化
						activitySet = new Activity[path_len];

						// パス作成
						int j = 0;
						for (int i = 0; i < path_len; i++) {
							for (; j < actSet.length;) {
								if (startOrder <= actSet[j].getAvailableOrder()
									&& endOrder >= actSet[j].getAvailableOrder()) {
									activitySet[i] = actSet[j];
									j++;
									break;
								}
								j++;
							}
						}

						if (reverse_flg) {
							this.reverseList();
						}
					}

					else { // 兄弟ではないActivity
						int st_path_count = 0;
						int ed_path_count = 0;

						// startActivityからルートActivityまでのActivityPathを取得
						ActivityPath st_path = new ActivityPath(startActivity, startActivity.getRoot());
						// ルートActivityからendActivityまでのActivityPathを取得
						ActivityPath ed_path = new ActivityPath(endActivity.getRoot(), endActivity);

						// startActivityとendActivityの共通親Activityを取得
						Activity p_act = ActivityPath.getCommonParent(startActivity, endActivity);

						// startActivityから共通親までのActivity数を数える
						for (int i = 0; i < st_path.activitySet.length; i++) {
							if (st_path.activitySet[i] == p_act) {
								st_path_count = i + 1;
								break;
							}
						}
						// 共通親からendActivityからまでのActivity数を数える
						for (int i = 0; i < ed_path.activitySet.length; i++) {
							if (ed_path.activitySet[i] == p_act) {
								ed_path_count = ed_path.activitySet.length - i;
								break;
							}
						}

						// ActivityPath作成
						// startActivityから共通親までのパス作成
						this.activitySet = new Activity[st_path_count + ed_path_count - 1];
						for (int i = 0; i < st_path_count; i++) {
							this.activitySet[i] = st_path.activitySet[i];
						}
						// 共通親からendActivityからまでのパス作成
						int j = ed_path.activitySet.length - ed_path_count + 1;
						for (int i = 0; i < ed_path_count - 1; i++) {
							this.activitySet[st_path_count + i] = ed_path.activitySet[j++];
						}
					}
				}
				else { // いずれかのActivityがroot
					Activity act;
					boolean reverse_flg = false;
					if (startActivity.isRoot() == true) {
						act = endActivity;
						reverse_flg = true;
					}
					else {
						act = startActivity;
					}

					// ルートActivityまでのActivity数を数える
					Activity p_act = act.getParent();
					int count;
					for (count = 1; p_act.isRoot() != true; count++) {
						p_act = p_act.getParent();
					}

					// ルートまでのActivityパスを作成
					activitySet = new Activity[count + 1];
					activitySet[0] = act;
					for (int i = 1; i <= count; i++) {
						activitySet[i] = activitySet[i - 1].getParent();
					}

					if (reverse_flg) {
						this.reverseList();
					}
				}

			// 作成したActivityパス内にAvailableでないActivityがある時はエラー
			for (int i = 0; i < activitySet.length; i++) {
				if (!activitySet[i].isAvailable()) {
					throw new SystemException("ActivityPath[ActivityPath()]:パスを作れません。AvailableでないActivityにパスを作ろうとしています。");
				}
			}

		}
		catch (Exception e) {
			throw new SystemException("ActivityPath[ActivityPath()]: パスを作れません。", e);
		}

	}

	/**
	 *	Activity同士での共通親Activityを取得
	 *	@param	act1
	 *			act2
	 *	@return 共通親Activity
	 */
	public static Activity getCommonParent(Activity act1, Activity act2) throws SystemException {

		Activity com_p_act = null;
		boolean break_flg = false;

		// いずれかのActivityがnullの時
		if (act1 == null || act2 == null) {
			return null;
		}

		// 同じActivity同士の時
		if (act1 == act2) {
			if (act1.isRoot()) {
				return null; // act1とact2がルートActivityの時は共通親としてnullを返す
			}
			return (act1.getParent());
		}

		// ルートActivityまでのActivityPathを取得
		ActivityPath act1_path = new ActivityPath(act1, act1.getRoot());
		ActivityPath act2_path = new ActivityPath(act2, act2.getRoot());

		// 取得したActivityPath同士の中から共通Activityを探す
		for (int i = 0; i < act1_path.activitySet.length; i++) {
			for (int j = 0; j < act2_path.activitySet.length; j++) {
				if (act1_path.activitySet[i] == act2_path.activitySet[j]) {
					com_p_act = act1_path.activitySet[i];
					break_flg = true;
					break;
				}
			}
			if (break_flg) {
				break;
			}
		}

		return com_p_act;
	}

	/**
	 *	ActivityPathの順番を逆にする
	 */
	public void reverseList() {

		if (activitySet == null) {
			return;
		}

		Activity tmp;
		int len = activitySet.length;
		int i, j;

		for (i = 0; i < len; i++) {
			tmp = activitySet[0];
			for (j = 0; j < len - (i + 1); j++) {
				activitySet[j] = activitySet[j + 1];
			}
			activitySet[j] = tmp;
		}
	}

	/**
	 *	ActivityPathの最初のActivityを削除
	 */
	public void removeFirst() {

		if (activitySet == null) {
			return;
		}

		int len = activitySet.length;

		if (len > 1) {
			Activity[] tmp = new Activity[len - 1];
			for (int i = 1; i < len; i++) {
				tmp[i - 1] = activitySet[i];
			}
			activitySet = new Activity[len - 1];
			activitySet = tmp;
		}
		else
			if (len == 1) {
				activitySet = null;
			}
	}

	/**
	 *	ActivityPathの最後のActivityを削除
	 */
	public void removeLast() {

		if (activitySet == null) {
			return;
		}

		int len = activitySet.length;

		if (len > 1) {
			Activity[] tmp = new Activity[len - 1];
			for (int i = 0; i < len - 1; i++) {
				tmp[i] = activitySet[i];
			}
			activitySet = new Activity[len - 1];
			activitySet = tmp;
		}
		else
			if (len == 1) {
				activitySet = null;
			}
	}

	/**
	 *	ActivityPathがあるかないか
	 *
	 * 	@return	true	アクティビティパスがない<BR>
	 *         	false	アクティビティパスがある<BR>
	 *
	 */
	public boolean isEmpty() {

		if (activitySet == null || activitySet.length == 0) {
			return true;
		}

		return false;
	}

	/**
	 *	ActivityPath内の次のActivityを返す
	 *	@return ActivityPath内の次Activity
	 */
	public Activity getNext() {

		if (activitySet == null) {
			return null;
		}

		if ((pos >= activitySet.length)) {
			return null;
		}

		pos++;

		return (activitySet[pos - 1]);
	}

	/**
	 *	Activity同士の前後関係
	 *
	 *  @param	act1
	 *		    act2
	 *	@return act1がact2より前、或いは同じなら真
	 *			 act1がact2より後なら偽
	 */
	public static boolean isForward(Activity act1, Activity act2) throws SystemException {

		try {
			// いずれかのActivityがnullの時
			if (act1 == null || act2 == null) {
				throw new SystemException("ActivityPath[isForward()]:null の Activity が指定されました。");
			}

			// 兄弟Activityの時
			if (act1.getParent() == act2.getParent()) {
				if (!act1.isRoot()) {
					if (act1.getAvailableOrder() >= act2.getAvailableOrder()) {
						return true;
					}
					return false;
				}
				return true;
			}

			// 兄弟Activity以外の時
			// 共通の親Activityを求める
			Activity p_act = ActivityPath.getCommonParent(act1, act2);
			if (p_act != null) {
				// 共通親ActivityまでのActivityPath作成
				ActivityPath act1_path = new ActivityPath(act1, p_act);
				ActivityPath act2_path = new ActivityPath(act2, p_act);

				// act1 が act2 の直系子孫の時
				if (act1_path.activitySet[act1_path.activitySet.length - 1] == act2) {
					return true;
				}
				// act2 が act1 の直系子孫の時
				if (act2_path.activitySet[act2_path.activitySet.length - 1] == act1) {
					return false;
				}

				// 共通親の直下Activity同士(=兄弟Activity)のオーダーを求める
				int act1_order = act1_path.activitySet[act1_path.activitySet.length - 2].getAvailableOrder();
				int act2_order = act2_path.activitySet[act2_path.activitySet.length - 2].getAvailableOrder();

				if (act1_order >= act2_order) {
					return true;
				}
				return false;
			}
			return true;
		}
		catch (Exception e) {
			throw new SystemException("ActivityPath[isForward()]: Activity同士の前後関係を判定できません。", e);
		}
	}

	/**
	 * アクティビティの位置関係を返す<BR>
	 *
	 *
	 * @param act1 基準のアクティビティ<BR>
	 * @param act2 ターゲットアクティビティ<BR>
	 *
	 * @return 以下の関係を返す
	 *      (1)SAME_ACTIVITY
	 *				act1 と act2 が同じアクティビティである<BR>
	 *      (2)SIBLING
	 *				act1 と act2 が兄弟アクティビティである<BR>
	 *      (3)ACT1_ACTIVITY_IS_COMMON_ANCESTOR
	 *				act1が共通の親である<BR>
	 *      (4)ACT2_ACTIVITY_IS_COMMON_ANCESTOR
	 *				act2が共通の親である<BR>
	 *		(5)ETC
	 *				その他<BR>
	 *
	 */
	public static int checkTarget(Activity act1, Activity act2) throws SystemException {

		try {
			if (act1 == null || act2 == null) {
				// いずれかのActivityがnullの時
				return (NULL_ACTIVITY);
			}

			if (act1 == act2) {
				// act1 と act2 が同じアクティビティの時
				return (SAME_ACTIVITY);
			}
			else
				if (act1.getParent() == act2.getParent()) {
					// act1 と act2 兄弟アクティビティの時
					return (SIBLING);
				}
				else {
					if (act1.isRoot()) {
						//act1が共通の親である
						return (ACT1_ACTIVITY_IS_COMMON_ANCESTOR);
					}
					else
						if (act2.isRoot()) {
							//act2が共通の親である
							return (ACT2_ACTIVITY_IS_COMMON_ANCESTOR);
						}
						else {
							// 共通の親Activityを求める
							Activity p_act = ActivityPath.getCommonParent(act1, act2);

							// 共通親ActivityまでのActivityPath作成
							ActivityPath act1_path = new ActivityPath(act1, p_act);
							ActivityPath act2_path = new ActivityPath(act2, p_act);

							if (act2_path.activitySet[act2_path.activitySet.length - 1] == act1) {
								// act2 が act1 の直系子孫の時 → act1が共通の親である
								return (ACT1_ACTIVITY_IS_COMMON_ANCESTOR);
							}
							else
								if (act1_path.activitySet[act1_path.activitySet.length - 1] == act2) {
									// act1 が act2 の直系子孫の時 → act2が共通の親である
									return (ACT2_ACTIVITY_IS_COMMON_ANCESTOR);
								}else{
									// その他
									return (ETC);
								}
						}
				}
		}
		catch (Exception e) {
			throw new SystemException("ActivityPath[checkTarget()]: Activityの位置関係を判定できません。");
		}
	}

	/**
	 * ActivityPathのカレントポジションが最後か否かを示す<BR>
	 * @return true 最後のアクティビティ<BR>
	 *         false 最後でない<BR>
	 */

	public boolean isLast() {
		if (pos >= activitySet.length) {
			return true;
		}
		return false;
	}

	/**
	 * ActivityPathのカレントポジションを最初の位置にセットしなおす<BR>
	 */
	public void setPos() {
		pos = 0;
	}
}
