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

//
//	ScormLoEndLearnServlet
//	変更履歴
//		2004.02.01	新規作成
//

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

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import java.util.Properties;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.ntt.lms.lo.LOException;
import jp.co.ntt.lms.xmo.util.DebugLog;

import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 * SCORM LO学習終了用サーブレット。<br>
 * SCORM LOサーバが学習の終了または中断を実行するときに、
 * SCORM LOサーバが呼び出します。
 * このサーブレットでは、送信されてきた情報をもとに、
 * ログ情報、中断情報、トレース情報を所定のデータベースまたはファイルに
 * 保存・登録して、実行結果を呼び出しもとのSCORM LOサーバに返答します。
 * 返答は、以下のXML形式となります。
 * 	<code>&lt;result&gt;処理結果&lt;/result&gt;</code>
 * 処理結果は、正常終了の場合&quot;OK&quot;、
 * 異常終了の場合&quot;NG&quot;となります。
 *
 * @author T.Nishiki
 */
public class ScormLoEndLearnServlet extends HttpServlet {

	/**
	 * GETリクエストを処理します。<br>
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
	 * @see ScormLoEndLearnServlet#doRequest(HttpServletRequest, HttpServletResponse)
	 */
	protected void doGet(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD doGet(reqeust,response)",
			DebugLog.ROW);

		doRequest(request, response);

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL doGet(reqeust,response)",
			DebugLog.ROW);
	}

	/**
	 * POSTリクエストを処理します。<br>
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
	 * @see ScormLoEndLearnServlet#doRequest(HttpServletRequest, HttpServletResponse)
	 */
	protected void doPost(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD doPost(reqeust,response)",
			DebugLog.ROW);

		doRequest(request, response);

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL doPost(reqeust,response)",
			DebugLog.ROW);
	}

	/**
	 * リクエストを処理します。<br>
	 *
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 */
	private void doRequest(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {

			////////////////////////////////////////////////////////////////////////
			// メソッド開始ログ
			DebugLog.write(getClass(),
				"HEAD doRequest(reqeust,response)",
				DebugLog.ROW);
			// 結果
			String status = "";
			// レスポンス用テキスト出力ストリーム。
			PrintWriter responseWriter = null;
			// リクエスト用入力ストリーム。
			InputStream requestStream = null;
			try {
				////////////////////////////////////////////////////////////////////
				ScormLoEnv scormEnv = new ScormLoEnv();
				// レスポンスのエンコードを取得
				String responseEncode = scormEnv.getProperty(
					ScormLoEnv.CHARSET_RESPONSE_END_LEARN);
				response.setContentType(
					"text/xml;charset="	+ responseEncode );
				// 書き込み開始
				responseWriter = response.getWriter();
				// XML宣言
				responseWriter.println(
					"<?xml version=\"1.0\" encoding=\"" + responseEncode + "\"?>");
				// リクエスト入力ストリームを取得しておきます。
				requestStream = request.getInputStream();
				////////////////////////////////////////////////////////////////////
				// 学習終了情報をリクエストパラメータから生成します。
				ScormLoEndLearnData endLearnData = makeScormLoEndLearnData(request);

			
			////////////////////////////////////////////////////////////////////
			// 終了理由が中止の場合、SCORM LO対して終了の通知だけ行う。
			String reason = endLearnData.getReason();

			// 2010.09.27 sbb taku-harada add >>
			// すでにファイル出力されているので以下のファイル出力処理は不要
			// 処理を通過しないよう強制的にreasonの値を変更
			reason = ScormLoConstant.PARAM_REASON_ABANDON;
			// 2010.09.27 sbb taku-harada add <<
			
			if (reason.equals(ScormLoConstant.PARAM_REASON_ABANDON) == false) {

				////////////////////////////////////////////////////////////////
				// LOアサイン上での学習回数のカウントアップ
				// countupAssignmentStudyCount(endLearnData, targetLOID);
				////////////////////////////////////////////////////////////////
				// 送信されたXMLデータをDOM Documentに変換
				Document document =
					ScormLoUtil.transformXmlStreamToDocument(requestStream);
				////////////////////////////////////////////////////////////////
				// 中断情報の部分を取得・保存
				Node resumeNode = XPathAPI.selectSingleNode(
						document, ScormLoConstant.XPATH_RESUME_NODE);
				if (resumeNode == null) {
					throw new IllegalArgumentException(
						"Not found Node["
						+ ScormLoConstant.XPATH_RESUME_NODE + "]");
				}
				// =============================================================
				// 中断情報出力先ファイル
				File resumeFile = ScormLoUtil.getResumeFile(
					endLearnData.getUserID(),
					endLearnData.getLoID(),
					endLearnData.getStudySession());
				// =============================================================
				// 中断情報XMLの出力オプション
				Properties resumeXmlProperties = new Properties();
				// エンコード
				String resumeFileEncode =
					scormEnv.getProperty(ScormLoEnv.CHARSET_RESUME_FILE);
				resumeXmlProperties.put(
					ScormLoUtil.PROPERTY_ENCODING, resumeFileEncode);
				// =============================================================
				// 中断Documentをファイルに変換・保存
				ScormLoUtil.transformNodeToFile(
					resumeNode, resumeFile, resumeXmlProperties);
				////////////////////////////////////////////////////////////////
				// トレース情報の部分を取得・保存
				// =============================================================
				Node traceNode = XPathAPI.selectSingleNode(
						document, ScormLoConstant.XPATH_TRACE_NODE);
				if (traceNode == null) {
					throw new IllegalArgumentException(
						"Not found Node["
						+ ScormLoConstant.XPATH_TRACE_NODE + "]");
				}
				// =============================================================
				// トレース情報出力先ファイル
				File traceFile = ScormLoUtil.getTraceFile(
					endLearnData.getUserID(),
					endLearnData.getLoID(),
					endLearnData.getLoginSession());
				// =============================================================
				// トレース情報XMLの出力オプション
				Properties traceXmlProperties = new Properties();
				// エンコード
				String traceFileEncode =
					scormEnv.getProperty(ScormLoEnv.CHARSET_TRACE_FILE);
				traceXmlProperties.put(
					ScormLoUtil.PROPERTY_ENCODING, traceFileEncode);
				// =============================================================
				// トレース情報をファイルに変換・保存
				ScormLoUtil.transformNodeToFile(
					traceNode, traceFile, traceXmlProperties);
				////////////////////////////////////////////////////////////////
				// 学習ログの取得・文字列に変換
				Node aiccLogNode = XPathAPI.selectSingleNode(
						document, ScormLoConstant.XPATH_AICCLOG_NODE);
				if (aiccLogNode == null) {
					throw new IllegalArgumentException(
						"Not found Node["
						+ ScormLoConstant.XPATH_AICCLOG_NODE + "]");
				}
				// AICC情報を変換
				String aiccLog =
					ScormLoUtil.transformNodeToString(aiccLogNode, null);
				endLearnData.setStudyLog( aiccLog );
			}

			// tryブロックの最後まできたら成功
			status = ScormLoConstant.ENDLEARN_STATUS_SUCCESS;
		}
		catch (LOException e) {
			status = ScormLoConstant.ENDLEARN_STATUS_FAILURE;
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			status = ScormLoConstant.ENDLEARN_STATUS_FAILURE;
		}

		////////////////////////////////////////////////////////////////////////
		// レスポンスに書込み
		responseWriter.print(
			"<" + ScormLoConstant.ENDLEARN_TAG_NAME_ROOT + ">");
		responseWriter.print(status);
		responseWriter.print(
			"</" + ScormLoConstant.ENDLEARN_TAG_NAME_ROOT + ">");
		responseWriter.close();
	}
	

	/**
	 * リクエストパラメータから学習終了情報を生成します。<br>
	 * あわせて各パラメータのNULLチェックも行います。<br>
	 * @param request HTTPリクエスト。
	 * @return パラメータから取得可能な学習終了情報。
	 * @throws LOException パラメータが送信されなかったり、
	 *          数値パラメータが数値に変換できなかったりした場合。
	 */
	private ScormLoEndLearnData makeScormLoEndLearnData(
			HttpServletRequest request) throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD makeScormLoEndLearnData(" + request + ")",
			DebugLog.ROW);
	
		
		ScormLoEndLearnData endLearnData = new ScormLoEndLearnData();
		try {
			////////////////////////////////////////////////////////////////////
			// リクエスト・パラメータ取得
			// 各パラメータともに取得後、ログ出力、NULLチェック、データクラス格納。
			//==================================================================
			// ユーザID
			String userID = request.getParameter(
					ScormLoConstant.PARAM_NAME_USERID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_USERID + "]"
				+ "[" + userID + "]", DebugLog.MID);
			if (userID == null) {
				throw new NullPointerException(
					"userID is not found at End-Learn");
			}
			endLearnData.setUserID(userID);
			//==================================================================
			// 教材のLOID
			String loID = request.getParameter(
					ScormLoConstant.PARAM_NAME_LOID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_LOID + "]"
				+ "[" + loID + "]", DebugLog.MID);
			if (loID == null) {
				throw new NullPointerException(
					"loID is not found at End-Learn");
			}
			endLearnData.setLoID(loID);
			//==================================================================
			// 教材の教材ID
			String materialID = request.getParameter(
					ScormLoConstant.PARAM_NAME_MATERIALID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_MATERIALID + "]"
				+ "[" + materialID + "]", DebugLog.MID);
			if (materialID == null) {
				throw new NullPointerException(
					"materialID is not found at End-Learn");
			}
			endLearnData.setMaterialID(materialID);
			//==================================================================
			// 学習セッション回数（開始−終了を１単位とする回数）
			String studySession = request.getParameter(
					ScormLoConstant.PARAM_NAME_STUDY_SESSION);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_STUDY_SESSION + "]"
				+ "[" + studySession + "]", DebugLog.MID);
			if (studySession == null) {
				throw new NullPointerException(
					"studySession is not found at End-Learn");
			}
			int intStudySession = Integer.parseInt(studySession);
			endLearnData.setStudySession(intStudySession);
			//==================================================================
			// ログインセッション回数（開始／再開−中断／終了を１単位とする回数）
			String loginSession = request.getParameter(
					ScormLoConstant.PARAM_NAME_LOGIN_SESSION);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_LOGIN_SESSION + "]"
				+ "[" + loginSession + "]", DebugLog.MID);
			if (loginSession == null) {
				throw new NullPointerException(
					"loginSession is not found at End-Learn");
			}
			int intLoginSession = Integer.parseInt(loginSession);
			endLearnData.setLoginSession(intLoginSession);
			//==================================================================
			// 終了理由
			String reason = request.getParameter(
					ScormLoConstant.PARAM_NAME_REASON);
			if (reason == null) {
				throw new NullPointerException(
					"reason is not found at End-Learn");
			}
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_REASON + "]"
				+ "[" + reason + "]", DebugLog.MID);
			endLearnData.setReason(reason);
			//==================================================================
			// 学習開始日時
			String startdate = request.getParameter(
					ScormLoConstant.PARAM_NAME_START_DATE);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_START_DATE + "]"
				+ "[" + startdate + "]", DebugLog.MID);
			if (startdate == null) {
				throw new NullPointerException(
					"startdate is not found at End-Learn");
			}
			endLearnData.setStartDate(startdate);
			//==================================================================
			// 完了状況の取得（終了理由が「終了」の場合に完了）
			String completionStatus = request.getParameter(ScormLoConstant.PARAM_NAME_COMPLETION);
			DebugLog.write(getClass(), "[" + ScormLoConstant.PARAM_NAME_COMPLETION + "]" + "[" + completionStatus + "]", DebugLog.MID);
			boolean completion = false;
			if (completionStatus != null) {
				completion = Boolean.valueOf(completionStatus).booleanValue();
			}
			endLearnData.setCompletionStatus(completion);
			//==================================================================
			// 合格状況の取得
			String satisfiedStatus = request.getParameter(ScormLoConstant.PARAM_NAME_SATISFIED);
			DebugLog.write(getClass(), "[" + ScormLoConstant.PARAM_NAME_SATISFIED + "]" + "[" + satisfiedStatus + "]", DebugLog.MID);
			boolean satisfied = false;
			if (satisfiedStatus != null) {
				satisfied = Boolean.valueOf(satisfiedStatus).booleanValue();
			}
			endLearnData.setSatisfiedStatus(satisfied);
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			throw new LOException(e.getMessage());
		}

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL makeScormLoEndLearnData(" + request + ")",
			DebugLog.ROW);

		return endLearnData;
	}	
}
