/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	ステータス伝搬クラス
//			変更履歴
//				2004.01.08	新規作成	二條智文
//
package jp.co.ntt.lms.Common.status;

// Java API
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.Vector;

import jp.co.ntt.lms.Common.status.conclusion.Conclusion;
import jp.co.ntt.lms.Common.status.conclusion.ConclusionException;
import jp.co.ntt.lms.Common.status.conclusion.ConclusionParseException;
import jp.co.ntt.lms.lo.LoLogDataAccess;
import jp.co.ntt.lms.lo.LoLogRecord;
import jp.co.ntt.lms.xmo.DataAccess;
import jp.co.ntt.lms.xmo.Lo.LoClient;
import jp.co.ntt.lms.xmo.LoRelation.LoRelationClient;

import org.apache.commons.lang.StringEscapeUtils;

/**
 * ステータス伝搬クラスです。<BR>
 * 指定されたLOIDのステータス更新処理および、得点、滞在時間の集計処理を行います。
 * @author 二條智文
 * @version 0.0.0.1
 */
public class StatusPropagation {

	/** ユーザIDを保持します */
	private String mstrUserID;

	/** LOIDを保持します */
	private String mstrLoID;

	/** RealLOIDを保持します。 */
	private String mstrRealLOID;

	/** LOタイプを保持します */
	private String mstrLoType;

	/** 親IDを保持します */
	private String mstrParentID;

	/** ステータス伝搬に関係するユーザIDリストを保持します */
	private String[] mstrUserList;

	/** データアクセスクラスを保持します */
	private DataAccess mobjDataAccess;

	/** UserIDリスト取得フラグを保持します */
	private String mstrListFlg;



	/**
	 * コンストラクタです。LOIDのみを指定してステータス伝搬処理を行います。<BR>
	 * ユーザは指定されたLOIDのステータスに関係するユーザのみに対してステータス伝搬を行います。
	 * @param strLoID LOID
	 * @exception PropagateException
	 */
	public StatusPropagation( String strLoID )
	{
		mstrLoID = strLoID;
		mstrListFlg = "0";
	}

	/**
	 * コンストラクタです。ユーザIDとLOIDを指定してステータス伝搬処理を行います。
	 * @param strUserID ユーザID
	 * @param strLoID LOID
	 */
	public StatusPropagation(	String strUserID,
								String strLoID)
	{

		mstrUserID = strUserID;
		mstrLoID = strLoID;
		mstrListFlg = "1";
	}


	/**
	 * ステータス伝搬メソッドです。
	 * コンストラクタで指定されたLOIDとユーザIDを元にステータスの
	 * 伝搬処理を行います。
	 * @exception PropagateException ステータス伝搬中に例外が発生した場合
	 * @exception SQLException データベースエラー
	 */
	public void propagate( DataAccess objDataAccess )
		throws PropagateException, SQLException
	{

		// LOIDがrootの場合は何も処理を行わない
		if( mstrLoID.equals( "LO0" ) ) {
			return;
		}

		mobjDataAccess = objDataAccess;

		if(mstrListFlg.equals("0")){
			LoLogDataAccess objLoLog = new LoLogDataAccess();
			mstrUserList = objLoLog.getUserList( mstrLoID, mobjDataAccess );
		}

		// コンストラクタでユーザIDが指定されずにUserIDリストを取得した場合は
		// UserIDリスト分ループしてステータス伝搬を行います
		if( mstrUserList != null ) {
			for( int i = 0; i < mstrUserList.length; i++ ) {
				// ユーザリスト分ステータス伝搬処理を繰り返す
				StatusPropagation objPropagation = new StatusPropagation(
										mstrUserList[i], mstrLoID );
				objPropagation.propagate( mobjDataAccess );
			}
			return;
		}


		// LOIDから親LOIDを取得します
		mstrParentID = getParentID(mstrLoID);
		// LOIDからLOタイプを取得します
		mstrLoType = getLoType(mstrLoID);

		// LOIDからRealLOIDを取得します。
		mstrRealLOID = getRealLOID( mstrLoID );
		if( mstrRealLOID == null ) {
			throw new PropagateException( "RealLOID is null. " + mstrLoID );
		}



		// 指定されてLOがコンテナの場合のみステータス更新処理をおこないます。
		if( mstrLoType.equals( LoClient.CONTAINER_TYPE ) ) {
			// ステータス更新処理
			updateStatus();
		}

		if( mstrParentID != null && !mstrParentID.equals( "LO0" ) ) {
			// 親LOへステータス伝搬再帰処理
			StatusPropagation objPropagation = new StatusPropagation(
										mstrUserID, mstrParentID );
			// 伝搬処理再帰
			objPropagation.propagate( mobjDataAccess );
		}
	}

	/**
	 * LOIDから親LOIDを取得するメソッドです。
	 * @exception SQLException 検索に失敗した場合
	 * @exception PropagateException 検索結果が一件もない場合
	 */
	private String getParentID(String strLoID)
		throws PropagateException, SQLException
	{
		String strParentID = null;

		// SQL組み立て
		StringBuffer strSQL = new StringBuffer();

		strSQL.append( "SELECT " );
		strSQL.append( LoRelationClient.PARENT_ID );
		strSQL.append( " FROM " );
		strSQL.append( LoRelationClient.TABLE_NAME );
		strSQL.append( " WHERE " );
		strSQL.append( LoRelationClient.CHILD_ID );
		strSQL.append( "='" );
		strSQL.append( StringEscapeUtils.escapeSql(strLoID) );
		strSQL.append( "'" );

		ResultSet rs = mobjDataAccess.executeQuery( new String(strSQL) );

		if( rs.next() ) {
			strParentID = rs.getString( LoRelationClient.PARENT_ID ).trim();
		}
		else {
			new PropagateException( "LO is not Exist! :: " + strLoID );
		}

		return strParentID;
	}

	/**
	 * 指定されたLOIDの自分を抜かした子IDリストを取得します。
	 * @param strParentID 親ID
	 * @return Vector 子IDリスト
	 * @exception SQLException DB例外
	 */
	private Vector getChildList(String strParentID)
		throws SQLException
	{
		Vector vecChildList = new Vector();

		// SQL組み立て
		StringBuffer strSQL = new StringBuffer();

		strSQL.append( "SELECT " );
		strSQL.append( LoClient.TABLE_NAME + "." + LoClient.REAL_LOID );
		strSQL.append( " AS " + LoClient.REAL_LOID );
		strSQL.append( " FROM " );
		strSQL.append( LoRelationClient.TABLE_NAME );
		strSQL.append( " INNER JOIN " );
		strSQL.append( LoClient.TABLE_NAME );
		strSQL.append( " ON " );
		strSQL.append( LoRelationClient.TABLE_NAME + "." + LoRelationClient.CHILD_ID );
		strSQL.append( " = " );
		strSQL.append( LoClient.TABLE_NAME + "." + LoClient.LO_ID );
		strSQL.append( " WHERE " );
		strSQL.append( LoRelationClient.PARENT_ID );
		strSQL.append( "='" );
		strSQL.append( StringEscapeUtils.escapeSql(strParentID) );
		strSQL.append( "'" );



		ResultSet rs = mobjDataAccess.executeQuery( new String(strSQL) );

		while( rs.next() ) {
			vecChildList.add(rs.getString( LoClient.REAL_LOID ).trim());
		}

		return vecChildList;
	}

	/**
	 * 指定されたLOIDの完結要件を調べ、完了しているかどうか調べます
	 * @return boolean true: 完了　false: 未完了
	 * @exception PropagateException 伝搬例外
	 */
	private boolean isCompleted()
		throws SQLException, PropagateException
	{
		// 完結要件を取得する
		String strConclusion = getConclusion(mstrLoID);

		if( strConclusion == null || strConclusion.equals( "" ) ) {
			// 完結要件が設定されていない場合
			// 子IDリストを取得する
			String strTempID = "";
			Vector vecChildList = getChildList( mstrLoID );
			for( int i = 0; i < vecChildList.size(); i++ ) {
				strTempID = (String)vecChildList.get(i);
				if( !isCompleted( strTempID ) ) {
					// ひとつでもfailedがあった場合ステータスはfaile
					return false;
				}
			}

			// ひとつもfailがない場合はcompleted
			return true;
		}
		else {
			// 完結要件クラスをインスタンス化
			Conclusion objConclusion = new Conclusion(	strConclusion,
														mstrUserID,
														mstrLoID,
														mobjDataAccess );

			boolean blIsConcluded = false;
			try {
				// 完結要件パース
				objConclusion.parse();
				blIsConcluded = objConclusion.isConcluded();
			}
			catch( ConclusionParseException cp ) {
				throw new PropagateException( cp.getMessage() );
			}
			catch( ConclusionException ce ) {
				throw new PropagateException( ce.getMessage() );
			}

			if( blIsConcluded ) {
				// 完結要件を満たしている場合はcomplete
				return true;
			}
		}

		return false;
	}

	/**
	 * 指定したLOIDのステータスが修了以上かどうかを調べます。
	 * @return boolean true: ステータスが修了以上 false: ステータスがfailed
	 * @exception SQLException DBエラー
	 */
	private boolean isCompleted(String strLoID )
		throws SQLException
	{
		LoLogDataAccess objLoLog = new LoLogDataAccess();

		return objLoLog.isCompleted(mstrUserID, strLoID, mobjDataAccess );
	}

	/**
	 * 指定されたLOIDの完結要件を調べ、合格しているかどうか調べます
	 * @return boolean true: 完了　false: 未完了
	 * @exception PropagateException 伝搬例外
	 */
	private boolean isPassed()
		throws SQLException, PropagateException
	{
		// 子IDリストを取得する
		String strTempID = "";
		Vector vecChildList = getChildList( mstrLoID );
		for( int i = 0; i < vecChildList.size(); i++ ) {
			strTempID = (String)vecChildList.get(i);
			if( !isPassed( strTempID ) ) {
				// ひとつでもfailedがあった場合ステータスはfaile
				return false;
			}
		}

		// ひとつもfailがない場合はPassed
		return true;
	}

	/**
	 * 指定したLOIDのステータスが修了以上かどうかを調べます。
	 * @return boolean true: ステータスが修了以上 false: ステータスがfailed
	 * @exception SQLException DBエラー
	 */
	private boolean isPassed(String strLoID )
		throws SQLException
	{
		LoLogDataAccess objLoLog = new LoLogDataAccess();

		return objLoLog.isPassed(mstrUserID, strLoID, mobjDataAccess );
	}

	/**
	 * 指定したLOIDの完結要件を取得します
	 * @param strLoID LOID
	 * @return String 完結要件
	 */
	private String getConclusion(String strLoID)
		throws PropagateException, SQLException
	{

		String strConclusion = null;

		// SQL組み立て
		StringBuffer strSQL = new StringBuffer();

		strSQL.append( "SELECT " );
		strSQL.append( LoClient.CONCLUSION );
		strSQL.append( " FROM " );
		strSQL.append( LoClient.TABLE_NAME );
		strSQL.append( " WHERE " );
		strSQL.append( LoClient.LO_ID );
		strSQL.append( "='" );
		strSQL.append( StringEscapeUtils.escapeSql(strLoID) );
		strSQL.append( "'" );

		ResultSet rs = mobjDataAccess.executeQuery( new String(strSQL) );

		if( rs.next() ) {
			strConclusion = rs.getString( LoClient.CONCLUSION );
			if( strConclusion != null ) {
				strConclusion = strConclusion.trim();
			}
		}
		else {
			new PropagateException( "LO is not Exist! :: " + strLoID );
		}

		return strConclusion;
	}

	/**
	 * 指定されたLOIDのLOタイプを取得します
	 * @param strLoID
	 * @exception PropagateException
	 * @exception SQLException
	 */
	private String getLoType( String strLoID )
		throws PropagateException, SQLException
	{
		String strLoType = null;

		// SQL組み立て
		StringBuffer strSQL = new StringBuffer();

		strSQL.append( "SELECT " );
		strSQL.append( LoClient.LO_TYPE );
		strSQL.append( " FROM " );
		strSQL.append( LoClient.TABLE_NAME );
		strSQL.append( " WHERE " );
		strSQL.append( LoClient.LO_ID );
		strSQL.append( "='" );
		strSQL.append( StringEscapeUtils.escapeSql(strLoID) );
		strSQL.append( "'" );

		ResultSet rs = mobjDataAccess.executeQuery( new String(strSQL) );

		if( rs.next() ) {
			strLoType = rs.getString( LoClient.LO_TYPE ).trim();
		}
		else {
			new PropagateException( "LO is not Exist! :: " + strLoID );
		}

		return strLoType;
	}

	/**
	 * 指定されたLOIDからRealLOIDを取得します。
	 * @param strLOID
	 * @return String RealLOID
	 * @exception PropagateException
	 * @exception SQLException
	 */
	private String getRealLOID( String strLoID )
		throws PropagateException, SQLException
	{
		String strRealLOID = null;

		// SQL組み立て
		StringBuffer strSQL = new StringBuffer();

		strSQL.append( "SELECT " );
		strSQL.append( LoClient.REAL_LOID );
		strSQL.append( " FROM " );
		strSQL.append( LoClient.TABLE_NAME );
		strSQL.append( " WHERE " );
		strSQL.append( LoClient.LO_ID );
		strSQL.append( "='" );
		strSQL.append( StringEscapeUtils.escapeSql(strLoID) );
		strSQL.append( "'" );

		ResultSet rs = mobjDataAccess.executeQuery( new String(strSQL) );

		if( rs.next() ) {
			strRealLOID = rs.getString( LoClient.REAL_LOID );
			if( strRealLOID != null ) {
				strRealLOID = strRealLOID.trim();
			}
		}
		else {
			new PropagateException( "LO is not Exist! :: " + strLoID );
		}

		return strRealLOID;
	}


	/**
	 * ステータス更新処理
	 * @param strLoID 更新対照LOID
	 */
	private void updateStatus()
		throws PropagateException, SQLException
	{

		LoLogDataAccess objLoLog = new LoLogDataAccess();

		// 学習開始日、累積学習時間、得点、最高得点、最低得点の
		// 合計を取得します。
		LoLogRecord objTotal = objLoLog.getTotal(	mstrUserID,
													mstrLoID,
													mobjDataAccess);
		// ユーザIDとLOIDを設定する
		objTotal.setUserID( mstrUserID );
		objTotal.setLoID( mstrRealLOID );


		// 既存のレコードを取得し現在のステータスを判断します
		LoLogRecord objRecord = objLoLog.getLoLog(	mstrUserID,
													mstrRealLOID,
													mobjDataAccess );

		// 合格ステータス
		boolean blPassed = false;

		// 完了ステータス
		boolean blCompleted = false;

		// 現在のステータスを判断します
		if( objRecord != null ) {

			// 合格ステータスの判断
			if( objRecord.getPassday() != null &&
				!objRecord.getPassday().equals( "" ) )
			{
				// 合格日付に日付が入っている場合は合格ステータスはtrue
				blPassed = true;
			}

			// 完了ステータスの判断
			if( objRecord.getStudyEndDate() != null &&
				!objRecord.getStudyEndDate().equals( "" ) )
			{
				// 完了日付に日付が入っている場合は完了ステータスはtrue;
				blCompleted = true;
			}

		}

		// 現在日付の設定
		Date objNowDate = new Date();

		// 現在の合格ステータスがfalseの場合、
		// 直下のLOの合格ステータスを調べ、合格していたら、
		// 合格日付に現在日付を設定します。
		if( !blPassed ) {
			if( isPassed() ) {

// 2005.03.10 LOステータスロールアップ処理変更 NTTレゾナント(Y.URATA) MOD start
				/*
				// 合格日付を設定
				objTotal.setPassday( XMODate.parseTime( objNowDate ) );
				*/							
			}else{
				// 直下のLOの合格ステータスを調べ、合格していなかったら、
				objTotal.setPassday( null );	
			}
		}else{
			// すでに合格日付が設定されていた場合は
			objTotal.setPassday( null );
		}
		
		// 現在の完了ステータスがfalseの場合
		// 完結要件から完了ステータスを調べ、完了していたら、
		// 完了日付に現在日付を設定します。
		if( !blCompleted ) {
			if( isCompleted() ) {
				/*
				// 修了日付を設定
				objTotal.setStudyEndDate( XMODate.parseTime( objNowDate ) );
				*/
			}else{
				// 完結要件から完了ステータスを調べ、完了していなかったら、
				objTotal.setStudyEndDate( null );
			}
		}else{
				// すでに終了日付が設定されていた場合
				objTotal.setStudyEndDate( null );
		}		
// 2005.03.10 MOD end


		// 学習開始日付の入れ替え
		if( objRecord != null ) {
			// すでに学習開始日付が設定されていた場合は
			// 学習開始日付は更新対象としない
			if( objRecord.getStudyStartDate() != null &&
				!objRecord.getStudyStartDate().equals("") )
			{
				objTotal.setStudyStartDate( null );
			}
		}
		// 更新処理
		objLoLog.insertUpdate( objTotal, mstrUserID, mstrRealLOID, mobjDataAccess );

	}
}


