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

//
// OfflineLO ログ更新クラス
//			変更履歴
//				2004.01.08	新規作成	二條　智文
//
package jp.co.ntt.lms.lo.Offline;


// Java API
import java.util.*;
import java.text.*;
import java.net.URL;
import java.io.IOException;
import java.sql.SQLException;

// xml API
import org.xml.sax.SAXException;

// XMS API
import jp.co.ntt.lms.xmo.util.*;
import jp.co.ntt.lms.xmo.*;
import jp.co.ntt.lms.lo.*;
import jp.co.ntt.lms.lo.Offline.Aicc.*;
import jp.co.ntt.lms.Common.status.*;
import jp.co.ntt.lms.xmo.Lo.LoClient;

/**
 * Offlineログ更新クラス
 * 本クラスは単独のThreadとして実行されます。
 */
public class OfflineLoLogControl extends Thread
	implements OfflineLoPropertyDef
{

	/** ユーザIDを保持します */
	private String mstrUserID = null;
	/** LOIDを保持します。 */
	private String mstrLoID = null;
	/** 教材IDをほじします。 */
	private String mstrMaterialID = null;
	/** ログのファイルパスを保持します。 */
	private String mstrLogPath = null;

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

	// AiccCoreテーブルのデータアクセスクラス
	AiccCoreDataAccess mobjAiccCoreDataAccess = new AiccCoreDataAccess();
	// SectionLogテーブルのデータアクセスクラス
	SectionLogDataAccess mobjSectionLogDataAccess = new SectionLogDataAccess();
	// AiccInteractionテーブルのデータアクセスクラス
	AiccInteractionDataAccess mobjInteractionDataAccess = new AiccInteractionDataAccess();
	// AiccCommentテーブルのデータアクセスクラス
	AiccCommentDataAccess mobjCommentDataAccess = new AiccCommentDataAccess();
	// AiccObjectivesテーブルのデータアクセスクラス
	AiccObjectivesDataAccess mobjObjectivesDataAccess = new AiccObjectivesDataAccess();
	// LoLogテーブルのデータアクセスクラス
	LoLogDataAccess mobjLoLogDataAccess = new LoLogDataAccess();
	// LologSessionテーブルのデータアクセスクラス
	LoLogSessionDataAccess mobjLoLogSessionDataAccess = new LoLogSessionDataAccess();

	// ログファイル出力
	private static final String LOG_PRE = "OfflineLO:ログアップロード処理:ログ更新処理:";
	private static final String START_INF_LOG = LOG_PRE + "処理を開始します。:";
	private static final String END_INF_LOG = LOG_PRE + "処理が終了しました。:";
	private static final String PARAM_ERR_LOG = LOG_PRE + "パラメータが不正です。:";
	private static final String FILEIO_ERR_LOG = LOG_PRE + "ログファイルの読み込みに失敗しました。:";
	private static final String FILEDELETE_ERR_LOG = LOG_PRE + "ログファイルの削除に失敗しました。:";
	private static final String PARSE_ERR_LOG = LOG_PRE + "パースエラー:";
	private static final String SYSTEM_ERR_LOG = LOG_PRE + "システムエラーが発生しました。:";
	private static final String DB_ERR_LOG = LOG_PRE + "データベースエラーが発生しました。:";

	/**
	 * コンストラクタです。
	 * @param strUserID ユーザID
	 * @param strLoID LOID
	 * @param strSessionID セッションID
	 */
	public OfflineLoLogControl( String strUserID, String strLoID, String strMaterialID, String strLogPath ) {
		mstrUserID = strUserID;
		mstrLoID = strLoID;
		mstrMaterialID = strMaterialID;
		mstrLogPath = strLogPath;
	}

	/**
	 * スレッド実行メソッド
	 */
	public void run() {
		// パラメータチェック
		if( mstrUserID == null ||
			mstrLoID == null ||
			mstrMaterialID == null ||
			mstrLogPath == null )
		{
			// @LOG >err >1 OfflineLO:ログアップロード処理:ログ更新処理:パラメータエラー
			DebugLog.write( this.getClass(),
							PARAM_ERR_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);

			// 処理終了
			return;
		}

		// @LOG >info >5 AD同期処理:ドメイン設定番号が設定されていません。
		DebugLog.write( this.getClass(),
						START_INF_LOG +
						mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
							DebugLog.MID);

		mobjDataAccess = new DataAccess();
		try {
			// トランザクションの開始
			mobjDataAccess.trans();

			insertLog();

			// トランザクションの終了
			mobjDataAccess.commit();

			// @LOG >info >5
			DebugLog.write( this.getClass(),
							END_INF_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.MID);

		}
		catch(IOException ie) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// ログ不整合時はログに出力
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							FILEIO_ERR_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), ie, DebugLog.HIGHT );
		}
		catch(AiccParseException ae) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// ログ不整合時はログに出力
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							PARSE_ERR_LOG + ae.getMessage() + ":" +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), ae, DebugLog.HIGHT );
		}
		catch(SAXException sae) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// XMLパースエラー時はログに出力
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							PARSE_ERR_LOG + sae.getMessage() + ":" +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), sae, DebugLog.HIGHT );
		}
		catch( SQLException se ) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// SQLエラー
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							DB_ERR_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
		}
		catch( LOException le ) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// LO固有エラー
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							SYSTEM_ERR_LOG + le.getMessage() + ":" +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), le, DebugLog.HIGHT );
		}

		catch( Exception ex ) {
			// ロールバック
			try { mobjDataAccess.rollback(); } catch(Exception e) {}

			// LO固有エラー
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							SYSTEM_ERR_LOG + ex.getMessage() + ":" +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), ex, DebugLog.HIGHT );
		}


		// ログファイルの削除処理を行います。
		try {
			// ログファイルの削除
			deleteLogFile();

		}
		catch( IOException ie ) {
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							FILEDELETE_ERR_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), ie, DebugLog.HIGHT );
		}
		catch( Exception ex ) {
			// @LOG >err >1
			DebugLog.write( this.getClass(),
							FILEDELETE_ERR_LOG +
							mstrUserID + ":" + mstrLoID + ":" + mstrMaterialID + ":" + mstrLogPath,
								DebugLog.HIGHT);
			DebugLog.write( this.getClass(), ex, DebugLog.HIGHT );
		}

	}

	/**
	 * Aiccログを解析して、Aiccの各テーブルに登録処理を行います。
	 *
	 * 処理内容：ステータスログクラスに
	 *           データを設定する。
	 * @exception LOException Lo固有の例外エラー
	 */
	public void insertLog()
		throws LOException, SQLException, AiccParseException, SAXException, IOException, Exception
	{

		///////////////////////////////////////////////////////////////
		// 個別ログテーブルの追加処理
		///////////////////////////////////////////////////////////////

		// Aiccログコンバートクラスをインスタンス化します
		AiccLogConverter objConvert = new AiccLogConverter( mstrUserID, mstrLoID, mstrMaterialID, mstrLogPath );

		// パースを行います
		objConvert.parse();

		// ログレコードを削除します。
		deleteLog(mstrUserID, mstrLoID);

		// AiccCoreテーブルの登録
		if( objConvert.getAiccCoreRecords() != null ) {
			mobjAiccCoreDataAccess.insert( objConvert.getAiccCoreRecords(), mobjDataAccess );
		}

		// SectionLogテーブルの登録
		if( objConvert.getSectionLogRecords() != null ) {
			mobjSectionLogDataAccess.insert( objConvert.getSectionLogRecords(), mobjDataAccess );
		}

		// AiccInteractionテーブルの登録
		if( objConvert.getAiccInteractionRecords() != null ) {
			mobjInteractionDataAccess.insert( objConvert.getAiccInteractionRecords(), mobjDataAccess );
		}

		// AiccCommentテーブルの登録
		if( objConvert.getAiccCommentRecords() != null ) {
			mobjCommentDataAccess.insert( objConvert.getAiccCommentRecords(), mobjDataAccess );
		}

		// AiccObjectivesテーブルの登録
		if( objConvert.getAiccObjectivesRecords() != null ) {
			mobjObjectivesDataAccess.insert( objConvert.getAiccObjectivesRecords(), mobjDataAccess );
		}

		// LoLogSessionテーブルの登録
		LoLogSessionRecord[] objSessions = objConvert.getLoLogSessionRecords();
		if( objSessions == null ) {
			throw new LOException( "セッションログ取得失敗" );
		}

		for( int i = 0; i < objSessions.length; i++ ) {
			insertLoLogSession( objSessions[i] );
		}

		///////////////////////////////////////////////////////////////
		// LoLogステータスログテーブルの更新処理
		///////////////////////////////////////////////////////////////
		LoLogRecord objRecord = objConvert.getLoLogRecord();

		if( objRecord == null ) {
			throw new LOException( "LOLog取得失敗" );
		}

		// LoLogSessionの一番大きいhighscoreを設定
		objRecord.setHighScore( mobjLoLogSessionDataAccess.getMaxHighScore( mstrUserID, mstrLoID, mobjDataAccess ) );
		// LoLogSessionの一番小さいlowscoreを設定
		objRecord.setLowScore( mobjLoLogSessionDataAccess.getMinLowScore( mstrUserID, mstrLoID, mobjDataAccess ) );
		// 直近のexecise_pointを設定
		objRecord.setExecisePoint( mobjLoLogSessionDataAccess.getNewestExcisePoint( mstrUserID, mstrLoID, mobjDataAccess ) );
		// LoLogSessionの一番大きいfullScoreを設定
		objRecord.setFullScore( mobjLoLogSessionDataAccess.getMaxFullScore( mstrUserID, mstrLoID, mobjDataAccess ) );
		// 合格日付の取得
		objRecord.setPassday( mobjLoLogSessionDataAccess.getFirstPassDay( mstrUserID, mstrLoID, mobjDataAccess ) );
		// 完了日付の取得
		objRecord.setStudyEndDate( mobjLoLogSessionDataAccess.getFirstStudyEndDate( mstrUserID, mstrLoID, mobjDataAccess ) );
		// 最終アクセス日時の取得
		String[] dateList = null;
		dateList = mobjLoLogSessionDataAccess.getStudyDateList( mstrUserID, mstrLoID, mobjDataAccess );
		objRecord.setLastAccess( dateList[0] );

		// LoLogテーブルの更新処理
		// 登録または更新処理を行います
		mobjLoLogDataAccess.insertUpdate(	objRecord,
											mstrUserID,
											mstrLoID,
											mobjDataAccess );

		// 参照LOIDリストを取得します
		LoClient objLoClient = new LoClient();
		if(!objLoClient.getListFromRealLOID( mstrLoID )){
			new LOException("参照LOIDリストの取得失敗しました。");
		}

		String[] strRefList = objLoClient.getRefListResult();

		// 参照リスト分ステータス伝播
		for(int i = 0; i < strRefList.length; i++){
			// ステータス伝搬処理
			StatusPropagation objStatus = new StatusPropagation( mstrUserID, strRefList[i] );
			PropagateControl.getInstance().addPropagate(objStatus);
		}
	}

	/**
	 * LoLogSessionテーブルにレコードを追加します。
	 * @param objSession LoLogSessionレコードデータ
	 * @exception SQLException
	 */
	private void insertLoLogSession( LoLogSessionRecord objSession )
		throws SQLException
	{

		String strSessionID = objSession.getSessionID();

		// 得点を集計して格納する
		// 最高得点の設定
		String strMaxScore = mobjAiccCoreDataAccess.getSumMaxScore(mstrUserID, mstrLoID, strSessionID, mobjDataAccess);
		// 最低得点の設定
		String strMinScore = mobjAiccCoreDataAccess.getSumMinScore(mstrUserID, mstrLoID, strSessionID, mobjDataAccess);
		// 得点の設定
		String strRawScore = mobjAiccCoreDataAccess.getSumRawScore(mstrUserID, mstrLoID, strSessionID, mobjDataAccess);
		// 満点の設定
		String strFullScore = mobjAiccCoreDataAccess.getFullScore( mstrUserID, mstrLoID, strSessionID, mobjDataAccess );


		// 最高得点の設定
		if( strMaxScore != null &&
			!strMaxScore.equals( "" ) )
		{
			objSession.setHighScore( strMaxScore );
		}
		// 最低得点の設定
		if( strMinScore != null &&
			!strMinScore.equals( "" ) )
		{
			objSession.setLowScore( strMinScore );
		}
		// 得点の設定
		if( strRawScore != null &&
			!strRawScore.equals( "" ) )
		{
			objSession.setExecisePoint( strRawScore );
		}
		// 満点の設定
		if( strFullScore != null &&
			!strFullScore.equals( "" ) )
		{
			objSession.setFullScore( strFullScore );
		}

		// 登録処理
		mobjLoLogSessionDataAccess.insertUpdate( objSession, mstrUserID, mstrLoID, strSessionID, mobjDataAccess );
	}

	/**
	 * ユーザIDとLOIDでログデータを削除します。
	 * <PRE>
	 *   削除テーブルは以下のもの
	 *     LoLog
	 *     LoLogSession
	 *     OL_AiccCore
	 *     OL_AiccInteraction
	 *     OL_AiccComments
	 *     OL_AiccObjectives
	 * </PRE>
	 * @param strUserID ユーザID
	 * @param strLoID LOID
	 * @param
	 * @exceptin SQLException DBアクセス例外
	 */
	private void deleteLog( String strUserID, String strLoID )
		throws SQLException
	{
		// OL_AiccCoreテーブルの同一キーデータを削除
		mobjAiccCoreDataAccess.delete( strUserID, strLoID, null, mobjDataAccess );
		// OL_SectionLogテーブルの同一キーデータを削除
		mobjSectionLogDataAccess.delete( strUserID, strLoID, null, mobjDataAccess );
		// OL_AiccInteractionテーブルの同一キーデータを削除
		mobjInteractionDataAccess.delete( strUserID, strLoID, null, mobjDataAccess );
		// OL_AiccCommentテーブルの同一キーデータを削除
		mobjCommentDataAccess.delete( strUserID, strLoID, null, mobjDataAccess );
		// OL_AiccObjectivesテーブルの同一キーデータを削除
		mobjObjectivesDataAccess.delete( strUserID, strLoID, null, mobjDataAccess );
		// LoLogテーブルの同一キーデータを削除
		mobjLoLogDataAccess.delete( strUserID, strLoID, mobjDataAccess );
		// LoLogSessionテーブルの同一キーデータを削除
		mobjLoLogSessionDataAccess.delete( strUserID, strLoID, mobjDataAccess );
	}

	/**
	 * ログファイルの削除を行います。
	 * 但し、ログレベルが10の時は削除を行いません
	 * @exception IOException ファイルIOエラー
	 * @exception Exception システムエラー
	 */
	private void deleteLogFile()
		throws IOException, Exception
	{
		// 環境設定ファイル読み込みクラスをインスタンス化
		Environment objEnv = new Environment();

		// ログレベルの取得
		String strLogLevel = objEnv.getLogLevel();
		int intLogLevelTemp = new Integer( strLogLevel ).intValue();

		if( intLogLevelTemp == 10 ) {
			return;
		}

		// ファイル削除処理
		FileControl.delete( mstrLogPath );
	}
}
