/*
このファイルの中身は、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) ______________________________________.
*/

//
//	Offline教材 学習管理クラス
//			変更履歴
//				2004.01.08	新規作成	鈴木　敏子

package jp.co.ntt.lms.lo.Offline;

import jp.co.ntt.lms.lo.*;
import jp.co.ntt.lms.xmo.util.*;
import jp.co.ntt.lms.xmo.*;
import jp.co.ntt.lms.Common.status.*;
import jp.co.ntt.lms.xmo.Lo.LoClient;
import jp.co.ntt.lms.xmo.Lo.LoData;
import jp.co.ntt.lms.xmo.People.PeopleClient;
import jp.co.ntt.lms.xmo.LoRelation.LoRelationClient;
import jp.co.ntt.lms.xmo.Base.XMSBaseClient;
import jp.co.ntt.lms.lo.Offline.*;

import java.sql.SQLException;
import java.util.*;
import java.io.*;

/**
 * 学習管理インタフェースクラス
 *
 * <PRE>
 *   Loの学習を管理する。
 *   LoServerクラスが所有する。
 *   下位に各Loの学習管理クラスを持つ。
 *   @version 2.0.4.0.0 2004.01.08
 *   @author H.Takishima
 * </PRE>
 *
 */
public class OfflineLearnManager
	implements LearnManager,OfflineLoPropertyDef
{

	/**
	* メッセージID
	*/
	/** ERRパラメータ不正 */
	private static final String ERR_PARAM_ID = "W001";
	/** ERRユーザ未登録 */
	private static final String ERR_NO_USER_ID = "W002";
	/** ERRLO未登録 */
	private static final String ERR_NO_LO_ID = "W003";
	/** ERRアサイン未登録 */
	private static final String ERR_ASIGNMENT_ID = "W004";
	/** ERR例外発生 */
	private static final String ERR_EXCEPTION_ID = "E001";

	/**
	* メッセージ
	*/
	/** ERRパラメータ不正 */
	private static final String ERR_PARAM = "ERRパラメータ不正";
	/** ERRユーザ未登録 */
	private static final String ERR_NO_USER = "ERRユーザ未登録";
	/** ERRLO未登録 */
	private static final String ERR_NO_LO = "ERRLO未登録";
	/** ERRアサイン未登録 */
	private static final String ERR_ASIGNMENT = "ERRアサイン未登録";
	/** ERR例外発生 */
	private static final String ERR_EXCEPTION = "ERR例外発生";

	/**
	* ログ出力メッセージ
	*/
	/** パラメータエラー */
	private static final String ERR_PARAM_LOG = "パラメータが不正です。";
	/** システムエラー */
	private static final String SYSTEM_ERR_LOG = "システムエラーが発生しました。";
	/** ユーザ未登録エラー */
	private static final String ERR_NO_USER_LOG = "ユーザが存在しません。";
	/** LO未登録未登録エラー */
	private static final String ERR_NO_LO_LOG = "LOが存在しません。";
	/** アサイン未登録エラー */
	private static final String ERR_ASIGNMENT_LOG = "権限がありません。";
	/** ログの保存エラー */
	private static final String UP_LOG_ERR_LOG = "ログの書き込みに失敗しました。";
	/** すべてのログの先頭につける */
	private static final String PREFIX = "OfflineLO：ログアップロード処理：Servlet：";

	/**
	 * 学習開始処理
	 *
	 * @param Hashtabel パラメータリスト
	 * @return Object 学習開始URL
	 * @exception LOException LO固有の例外エラー
	 */
	public Object startLearn(Hashtable htbParam, DataAccess objDataAccess)
		throws LOException
	{
		// ログ出力処理を実行します。
		startUpdateLog(htbParam, objDataAccess);

		// 学習開始処理を実行します。
		return null;
	}

	/**
	 * 学習終了処理
	 *
	 * @param Hashtabel パラメータリスト
	 * @return Object NULL
	 * @exception LOException Lo固有の例外エラー
	 */
	public Object endLearn(Hashtable htbParam, DataAccess objDataAccess)
		throws LOException
	{
		// 学習終了処理を実行します。
		return endLearnAction(htbParam, objDataAccess);
	}

	/**
	 * 学習開始時のログ出力処理を実行します。
	 */
	protected void startUpdateLog(Hashtable htbParam, DataAccess objDataAccess)
		throws LOException
	{
		try
		{
			String strUserID = (String)htbParam.get(USER);
			String strLoID = (String)htbParam.get(LOID);

			// 本日日付の取得
			String strNow = XMODate.parseTime( new java.util.Date() );

			// LoLogSessionテーブルの取得処理
			LoLogSessionDataAccess objSessionDataAccess = new LoLogSessionDataAccess();

			// LoLogSessionの存在チェック
			int intStudyCount = objSessionDataAccess.getStudyCount(	strUserID, strLoID);

			if( intStudyCount == 0 ) {

				///////////////////////////////////////////////////////////////
				// LoLogステータスログテーブルの更新処理
				///////////////////////////////////////////////////////////////
				LoLogDataAccess objLoLogDataAccess = new LoLogDataAccess();

				LoLogRecord objDbData = new LoLogRecord();

				// ユーザIDの設定
				objDbData.setUserID( strUserID );

				// LOIDの設定
				objDbData.setLoID( strLoID );

				// 開始日付の設定
				objDbData.setStudyStartDate( strNow );

				// 累積滞在時間の設定
				objDbData.setResidanceTime( "0" );

				// アクセス回数の設定
				objDbData.setAccessCnt( "1" );

				// 最終アクセス日時の設定
				objDbData.setLastAccess( strNow );

				// ステータスログの登録または更新を行います
				objLoLogDataAccess.insertUpdate(	objDbData,
													strUserID,
													strLoID,
													objDataAccess );

				// 参照LOIDリストを取得します
				LoClient objLoClient = new LoClient();
				if(!objLoClient.getListFromRealLOID( strLoID )){
					new LOException("参照LOIDリストの取得失敗しました。");
				}
				String[] strRefList = objLoClient.getRefListResult();
				// 参照リスト分ステータス伝播
				for(int i = 0; i < strRefList.length; i++){
					//ステータス伝搬処理
					StatusPropagation objStatus = new StatusPropagation( strUserID, strRefList[i] );
					PropagateControl.getInstance().addPropagate(objStatus);
				}

				///////////////////////////////////////////////////////////////
				// LoLogセッションログテーブルの更新処理
				///////////////////////////////////////////////////////////////
				LoLogSessionRecord objSession = new LoLogSessionRecord();

				// ユーザID設定
				objSession.setUserID( strUserID );

				// LOID設定
				objSession.setLoID( strLoID );

				// セッションIDの設定
				objSession.setSessionID( "1" );

				// 開始日
				objSession.setStudyStartDate( strNow );

				// 累積滞在時間
				objSession.setResidanceTime( "0" );

				// 合格日
				objSession.setPassday( null );

				// 修了日
				objSession.setStudyEndDate( null );

				// 最高得点
				objSession.setHighScore( null );

				// 最低得点
				objSession.setLowScore( null );

				// 得点
				objSession.setExecisePoint( null );

				// 満点
				objSession.setFullScore( null );

				// 登録処理
				objSessionDataAccess.insertUpdate( objSession, strUserID, strLoID, "1", objDataAccess );
			}
		}
		catch(SQLException se)
		{
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			throw new LOException( se.getMessage() );
		}
		catch(Exception e)
		{
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			throw new LOException(e.getMessage());
		}
	}

	/**
	 * 学習終了処理
	 *
	 * @param Hashtabel パラメータリスト
	 * @return Object NULL
	 * @exception LOException Lo固有の例外エラー
	 */
	synchronized protected Object endLearnAction(Hashtable htbParam, DataAccess objDataAccess)
		throws LOException
	{
		Object objRet = null;

		try
		{
			// Offline教材詳細情報を設定します
			String strUserID = null;
			Vector vecMessage = new Vector();	// メッセージ

			// パラメータを取得します
			String strForeignUserID = (String)htbParam.get( "ForeignID" );	// 外部ユーザID
			String strMaterialID = (String)htbParam.get( "MaterialID" );	// 教材ID
			String strOfflineLog = (String)htbParam.get( "OfflineLog" );	// オフラインログ

			// パラメータチェック
			if( strForeignUserID == null ||
				strMaterialID == null ||
				strOfflineLog == null )
			{
DebugLog.write( this.getClass(), PREFIX + ERR_PARAM_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// パラメータが不正の場合はエラーメッセージを返す。
				MessageData objMd = new MessageData(ERR_PARAM_ID,
													ERR_PARAM,
													MessageData.WAR_TYPE);
				throw new LOException(objMd);
			}

			//---------------------------------------
			// 外部ユーザIDからユーザIDを取得します。
			//---------------------------------------
			PeopleClient objPeople = new PeopleClient();
			// 検索条件を設定
			objPeople.setWhereForeignID(strForeignUserID);
			if( !objPeople.select() ) {
DebugLog.write( this.getClass(), PREFIX + SYSTEM_ERR_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// 検索がうまくいかなかった場合、例外発生メッセージを返す
				MessageData objMd = new MessageData(ERR_EXCEPTION_ID,
													ERR_EXCEPTION,
													MessageData.ERR_TYPE);
				throw new LOException(objMd);
			}

			// Peopleテーブルから外部ユーザIDのレコードを取得できた場合
			if( objPeople.next() ) {
				strUserID = objPeople.getUserID();
			}
			// Peopleテーブルから外部ユーザIDのレコードを取得でなかった場合
			else{
DebugLog.write( this.getClass(), PREFIX + ERR_NO_USER_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// ユーザ未登録の場合はエラーメッセージを返す。
				MessageData objMd = new MessageData(ERR_NO_USER_ID,
													ERR_NO_USER,
													MessageData.WAR_TYPE);
				throw new LOException(objMd);
			}

			// 教材IDからRealLOIDを取得します。
			OfflineLoDataAccess objOfflineLoDataAccess = new OfflineLoDataAccess();
			String strRealLOID = objOfflineLoDataAccess.getLOIDFromMaterial(strMaterialID, objDataAccess);
			if(strRealLOID == null){
DebugLog.write( this.getClass(), PREFIX + ERR_NO_LO_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// LO未登録の場合はエラーメッセージを返す
				MessageData objMd = new MessageData(ERR_NO_LO_ID,
													ERR_NO_LO,
													MessageData.WAR_TYPE);
				throw new LOException(objMd);
			}

			// RealLOIDから参照LOIDリストを取得します。
			LoClient objLo = new LoClient();
			String[] strLOIDList;
			// 検索条件を設定
			boolean boolRet = objLo.getListFromRealLOID(strRealLOID);
			if( !boolRet ) {
DebugLog.write( this.getClass(), PREFIX + SYSTEM_ERR_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// 検索がうまくいかなかった場合、例外発生メッセージを返す
				MessageData objMd = new MessageData(ERR_EXCEPTION_ID,
													ERR_EXCEPTION,
													MessageData.ERR_TYPE);
				throw new LOException(objMd);
			}
			// LOテーブルからLOIDのレコードを取得できた場合
			else{
				strLOIDList = objLo.getRefLstResult();
				// 参照がない場合
				if(strLOIDList.length == 0){
DebugLog.write( this.getClass(), PREFIX + SYSTEM_ERR_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
					// 参照がない場合、LO未登録メッセージを返す
					MessageData objMd = new MessageData(ERR_EXCEPTION_ID,
														ERR_EXCEPTION,
														MessageData.ERR_TYPE);
					throw new LOException(objMd);
				}
			}

			//---------------------
			// 学習アサインチェック
			//---------------------
			boolean boolAuth = false;
			for( int i = 0; i < strLOIDList.length; i++ ){
				if(checkAuth(strUserID, strLOIDList[i])){
					boolAuth = true;
					break;
				}
			}
			// LOIDリストのうち1つも学習アサインされていない場合はアサインエラーを返却します。
			if(!boolAuth){
DebugLog.write( this.getClass(), PREFIX + ERR_ASIGNMENT_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// アサイン未登録メッセージを返す
				MessageData objMd = new MessageData(ERR_ASIGNMENT_ID,
													ERR_ASIGNMENT,
													MessageData.WAR_TYPE);
				throw new LOException(objMd);
			}


			//-------------
			// ログ保存処理
			//-------------
			// ログ格納フォルダパスの取得
			// OfflineLoEnv読込
			OfflineLoEnv objOfflineEnv = new OfflineLoEnv();
			String strUpLogPath = objOfflineEnv.getUpLogPath();	// アップロードログ格納フォルダパス

			// ログの保存
			// ファイルパスの生成
			String strNow = XMODate.parseYYYYMMDDHHmmss( new java.util.Date() );	// 本日日付
			String strFilePath = strUpLogPath + "/" + strUserID + "/" + strRealLOID.trim() + "_" + strNow + ".xml";
			boolean boolLog = outPut(strOfflineLog, strFilePath, false);
			// ログの書き込みに失敗した場合
			if(boolLog == false){
DebugLog.write( this.getClass(), PREFIX + UP_LOG_ERR_LOG + ":" + strForeignUserID + ":" + strMaterialID, DebugLog.HIGHT);
				// 例外発生メッセージを返す
				MessageData objMd = new MessageData(ERR_EXCEPTION_ID,
													ERR_EXCEPTION,
													MessageData.ERR_TYPE);
				throw new LOException(objMd);
			}

			//-------------
			// ログ更新処理
			//-------------
			OfflineLoLogControl objLoLogControl = new OfflineLoLogControl( strUserID, strRealLOID, strMaterialID, strFilePath );
			objLoLogControl.start();

		}
		catch(LOException le)
		{
			throw le;
		}
		catch(Exception e)
		{
			// 例外発生メッセージを返す
			MessageData objMd = new MessageData(ERR_EXCEPTION_ID,
												ERR_EXCEPTION,
												MessageData.ERR_TYPE);
			throw new LOException(objMd);
		}

		return objRet;
	}

	/**
	 * 権限チェック
	 *  以下のチェックをする
	 *    LOのアサインチェック
	 *    指定されたLOがアサインされていない場合はエラーとする
	 * @return boolean true: 権限有り false: 権限なし
	 */
	private boolean checkAuth( String strUserID, String strLoID )
		throws LOException, Exception
	{
		// ユーザIDがスーパユーザの場合は無条件にOK
		if( strUserID.equals(XMOPropertyDef.ROOT_USER) ) {
			return true;
		}

		// 権限チェックフラグ
		boolean isAuth = false;

		// 権限対象LOのリストを取得します。
		// XMSBaseを使用して、ログインユーザが使用できるLOリストを取得します。
		XMSBaseClient objBase = new XMSBaseClient();
		if( !objBase.searchRootLoList( strUserID, jp.co.ntt.lms.xmo.Base.XMSBaseTable.LEARN_TYPE ) ) {
			throw new LOException( objBase.getMessage() );
		}

		LoData[] objRootLo = objBase.getRootLoList();
		// 権限フィルタ用のIDリストを作成する。
		Vector vecAuthList = new Vector();
		for( int i = 0; i < objRootLo.length; i++ ) {
			vecAuthList.add( objRootLo[i].getLoID() );
		}

		// 指定LOIDのパスを取得します。
		// 参照LOIDのパスを取得する
		LoRelationClient objLoRelation = new LoRelationClient();
		if( !objLoRelation.searchPath(strLoID) ) {
			// パス検索がうまくいかなかった場合
			throw new LOException( objLoRelation.getMessage() );
		}

		// パス毎に権限チェックを行う。
		while( objLoRelation.next() ) {
			String[] strPath = objLoRelation.getPath();
			for( int i = 0; i < strPath.length; i++ ) {

				// 権限フィルターが設定されていた場合は権限によるチェックをします。
				if( vecAuthList.contains( strPath[i] ) ) {
					// 権限フィルターにIDが含まれていたら権限フラグをtrueにする
					isAuth = true;
					break;
				}

			}

			if( isAuth ) {
				break;
			}
		}

		return isAuth;
	}

	/**
	 * 指定したファイルの内容を出力先ファイルに追加します。<BR>
	 * 出力先ファイルが存在しない場合はファイルを作成します。。
	 * @param strOutputData XML形式に編集されたデータ
	 * @param strFileTo 出力先ファイルパス<BR>
	 * @return int ファイル出力の結果を返します
	 * @exception IOException ファイル出力に失敗した場合
	 */
	private boolean outPut( String strOutputData, String strFileTo, boolean isAppend )
		throws IOException
	{
		boolean boolRet = false;
		OutputStreamWriter out = null;

		try {
			// 出力先フォルダを作成する
			File objToFile = new File( strFileTo );
			File objParentDir = objToFile.getParentFile();
			objParentDir.mkdirs();

			FileOutputStream fileout = new FileOutputStream( strFileTo );

			out = new OutputStreamWriter( fileout, "UTF-8" );

			out.write( strOutputData, 0, strOutputData.length() );
			out.flush();
			out.close();
		} catch (IOException e){
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			if (out != null){
				out.close();
			}
			return boolRet;
		} catch (Exception e){
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			return boolRet;
		}
		// 出力先のファイルを閉じる
		out.close();

		// 正常終了
		boolRet = true;

		return boolRet;
	}

}

