/*
 * 学習開始クラス
 * 
 * 作成日: 2006/10/23
 */

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

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import jp.co.ntt.lms.imode.common.MobConst;
import jp.co.ntt.lms.imode.common.MobileUtl;
import jp.co.ntt.lms.imode.extension.PageSequence;
import jp.co.ntt.lms.lo.util.UserIDFolder;
import jp.co.ntt.lms.lo.scorm.engine.Engine;
import jp.co.ntt.lms.lo.scorm.engine.ExecuteParameters;
import jp.co.ntt.lms.lo.scorm.engine.SCORMAPIAdapter;
import jp.co.ntt.lms.lo.scorm.engine.TimeoutWatcher;
import jp.co.ntt.lms.lo.scorm.util.Authentication;
import jp.co.ntt.lms.lo.scorm.util.Debug;
import jp.co.ntt.lms.lo.scorm.util.SG;
import jp.co.ntt.lms.lo.scorm.util.XMLUtil;
import jp.co.ntt.lms.lo.scorm.util.XPathUtil;
import jp.co.ntt.lms.xmo.util.DebugLog;
import jp.co.ntt.lms.xms.framework.XmsException;
import jp.co.ntt.lms.xms.framework.XmsRequest;
import jp.co.ntt.lms.xms.framework.XmsSystemException;

import org.w3c.dom.Document;


/**
 * 学習開始クラス
 * 
 * @author ntc-sakai
 */
public class StartEngine {

	/** 実行結果 */
	private boolean status;

	/** ２重起動 */
	private boolean isDuplication = false;

	/** 頁SEQエラー */
	private boolean isPageSeq = false;

	/**
	 * 学習開始実行
	 * 
	 * @param xmsRequest xmsRequestクラス
	 * @throws XmsException
	 * 
	 */
	public void request(XmsRequest xmsRequest) throws XmsException {
		// ＬＯＧ出力	
		DebugLog.write(getClass(), "[request]HEAD", DebugLog.ROW);

		HttpServletRequest request = xmsRequest.getRequest();
		HttpSession session = request.getSession();
		ServletContext application = session.getServletContext();

		try {
			// 入力パラメータ取得（リターンURL）
			String returnURL = request.getParameter("returnURL");

			if (MobileUtl.isEmpty(returnURL)) {
				// 戻り先URLパラメータ無
				DebugLog.write(getClass(), "[request]returnURL not found ", DebugLog.HIGHT );
				// 学習開始状態をエラーに設定する
				status = false;
				return;
			}

			// 起動パラメータを作成する
			ExecuteParameters executeParameters = new ExecuteParameters();

			executeParameters.initialize(xmsRequest);

			// -----------------------
			// チェック処理
			// -----------------------

			// 認証を行う
			if (!Authentication
				.authenticate(
					executeParameters.getUserID(),
					executeParameters.getLOID(),
					xmsRequest.getParameter("AuthKey"))) {
				// 認証エラー
				DebugLog.write(getClass(), "[request]AuthenticationError UserId = "
						+ executeParameters.getUserID() + " LOID = "
						+ executeParameters.getLOID(), DebugLog.HIGHT );
				// 学習開始状態をエラーに設定する
				status = false;
				return;
			}

			// エンジンをapplicationに存在するかどうかをチェックする
			String engineKeyName =
				"SCORM." + executeParameters.getUserID() + "." + executeParameters.getLOID() + ".ENGINE";

			// エンジンがapplicationに存在する場合
			if (application.getAttribute(engineKeyName) != null) {
				// セッションから頁ＳＥＱ管理クラス取得
				PageSequence pageSeq = (PageSequence) session.getAttribute(MobConst.S_PAGE_SEQ);
				if (pageSeq == null) {
					// セッションに頁ＳＥＱ管理クラスが存在しない場合、２重起動エラー
					// 強制終了処理
					ForceSuspendLearn fsl = new ForceSuspendLearn(engineKeyName);
					fsl.request(xmsRequest);
					if (!fsl.isStatus()) {
						// 強制終了処理エラー
						DebugLog.write(getClass(), "[request]LearningLogSendError engineKey = "
								+ engineKeyName, DebugLog.HIGHT );
					}
					// 戻りＵＲＬをsessonに設定する
					session.setAttribute("MOBILE.RETURN_URL", returnURL);
					isDuplication = true;
					isPageSeq = false;
				} else {
					// セッションに頁ＳＥＱ管理クラスが存在する場合、頁ＳＥＱエラー
					isDuplication = false;
					isPageSeq = true;
				}
				// 学習開始状態をエラーに設定する
				status = false;
				return;
			}

			// 各チェック正常時
			// 中断情報 + 教材構造をXMSⅡから受信する
			StringBuffer studyInfoURL = new StringBuffer();

			studyInfoURL.append(executeParameters.getStudyInfoURL());
			studyInfoURL.append("?USERID=");
			studyInfoURL.append(executeParameters.getUserID());
			studyInfoURL.append("&LOID=");
			studyInfoURL.append(executeParameters.getLOID());
			studyInfoURL.append("&StudySession=");
			studyInfoURL.append(executeParameters.getStudySessionCount());
			studyInfoURL.append("&COMD=");
			studyInfoURL.append(executeParameters.getStartCommand());
			studyInfoURL.append("&AuthKey=");
			studyInfoURL.append(
				Authentication.createKey(
					null,
					executeParameters.getUserID(),
					executeParameters.getLOID(),
					SG.get("ManagerName")));

			// 中断情報 + 教材構造Document
			Document doc;

			try {
				doc = XMLUtil.newDocumentBuilder(false).parse(studyInfoURL.toString());
			}
			catch (Exception ex) {
				DebugLog.write(getClass(), "[request]Manifest File parse error", DebugLog.HIGHT);
				Debug.write(ex);
				status = false;
				return;
			}

			// 取得結果をチェックする
			String studyInfoStatus;
			studyInfoStatus = XPathUtil.selectSimpleSingleNode(doc.getDocumentElement(), "status/text()").getNodeValue();

			// 中断情報 + 教材構造取得エラーが発生した場合
			if (!studyInfoStatus.equals("0")) {
				DebugLog.write(getClass(), "[request]Manifest File parse error(status = "
						+ studyInfoStatus + ")", DebugLog.HIGHT);
				status = false;
				return;
			}
			// 教材構造をDOM Documentにする
			Document manifest =
				XMLUtil.createDocument(XPathUtil.selectSingleChildNode(doc.getDocumentElement(), "manifest"), false);

			// 中断情報をDOM Documentにする
			Document resume = null;

			// 再開時のみ中断情報を取得する
			if (executeParameters.getStartCommand().equals("CONT")) {
				// 中断情報をDOM Documentにする
				resume =
					XMLUtil.createDocument(
						XPathUtil.selectSingleChildNode(doc.getDocumentElement(), "resume"),
						false);
			}

			// エンジンを作成する
			Engine engine = new Engine(executeParameters);

			// エンジンを初期化する
			try {
				engine.initialize(manifest, resume);
			} catch (Exception e) {
				DebugLog.write(getClass(), "[request]engine initialize error", DebugLog.HIGHT);
				Debug.write(e);
				status = false;
				return;
			}

			// navtrace.xmlのファイルを作成し、xml宣言を追加する
			StringBuffer navtraceLogFile = new StringBuffer();

			navtraceLogFile.append(userIDPath(SG.get("ResumeDataBaseURL"), executeParameters.getUserID()));
			navtraceLogFile.append(File.separator);
			navtraceLogFile.append(executeParameters.getLOID());
			navtraceLogFile.append(File.separator);
			navtraceLogFile.append(executeParameters.getStudySessionCount());
			navtraceLogFile.append(File.separator);
			navtraceLogFile.append(executeParameters.getLaunchCount());
			navtraceLogFile.append(File.separator);

			// navtrace.xmlを格納するフォルダが存在しない場合は作成する
			File file = new File(navtraceLogFile.toString());

			if (!file.exists()) {
				file.mkdirs();
			}

			navtraceLogFile.append("navtrace.xml");

			PrintWriter pw = new PrintWriter(new FileWriter(navtraceLogFile.toString()));

			try {
				pw.println("<?xml version=\"1.0\" encoding=\"" + SG.get("XMLEncoding") + "\"?>");
			}
			finally {
				pw.close();
			}

			// 頁SEQクラス
			PageSequence pageSeq = new PageSequence();
			// 携帯ＳＣＯＲＭＢｅａｎクラス生成
			MobSCORMBean scormbean = new MobSCORMBean(executeParameters.getManifestID());
			// タイムアウト監視クラス、起動パラメータ、api adapterをsessionに設定する
			session.setAttribute("SCORM.TIMEOUT_WATCHER", new TimeoutWatcher(executeParameters));
			session.setAttribute("SCORM.EXECUTE_PARAMETERS", executeParameters);
			session.setAttribute("SCORM.API_ADAPTER", new SCORMAPIAdapter(executeParameters));

			// 戻りＵＲＬをsessonに設定する
			session.setAttribute("MOBILE.RETURN_URL", returnURL);
			// 頁SEQクラスをsessionに設定する
			session.setAttribute(MobConst.S_PAGE_SEQ, pageSeq);
			// 携帯ＳＣＯＲＭＢｅａｎをsessionに設定する。
			session.setAttribute(MobConst.S_SCORM_BEAN, scormbean);
			// エンジンをapplicationに設定する
			application.setAttribute(engineKeyName, engine);
			// 学習開始状態に正常を設定する
			status = true;

			// ＬＯＧ出力
			DebugLog.write(getClass(), "[request]TAIL", DebugLog.ROW);

		} catch (Exception e) {
			// ＬＯＧ出力
			DebugLog.write(getClass(), "[request]Exception error", DebugLog.ROW);
			Debug.write(e);
			throw new XmsSystemException(e);
		}

		
	}

	/**
	 * エンジン開始実行結果を戻します。
	 * @return status  正常の場合true エラーの場合false
	 */
	public boolean isStatus() {
		return status;
	}

	/**
	 * ２重起動の有無を戻します。
	 * 
	 * @return isDuplication ２重起動の場合true 正常の場合false
	 */
	public boolean isDuplication() {
		return isDuplication;
	}

	/**
	 * 頁SEQエラー状態を戻します。
	 * 
	 * @return isPageSeq エラーの場合true 正常の場合false
	 */
	public boolean isPageSeq() {
		return isPageSeq;
	}

	private static String userIDPath(String strDirPath, String strUserID)
	{
		return UserIDFolder.getPath(strDirPath, strUserID);
	}
}
