/*
このファイルの中身は、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 新規作成		A.Shimoma
//
package jp.co.ntt.lms.xms.logsumup;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.xmo.DataAccess;
import jp.co.ntt.lms.xmo.GroupAndPeople.GroupAndPeopleClient;
import jp.co.ntt.lms.xms.framework.XmsException;
import jp.co.ntt.lms.xms.framework.XmsInnerException;
import jp.co.ntt.lms.xms.framework.XmsSystemException;
import jp.co.ntt.lms.xms.framework.base.Logic;
import jp.co.ntt.lms.xms.framework.base.LogicResult;
import jp.co.ntt.lms.xmo.Environment;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * ログ集計グループ比較集計ロジック・クラス。<br>
 * 
 * @author shimoma
 */
public class LoLogGroupCompareLogic implements Logic {

	/**
	 * リクエストされたグループIDのリスト
	 */
	private List groupList = new ArrayList();

	/**
	 * リクエストされたLoID
	 */
	private String LoID;

	/**
	 * リクエストされたグループID
	 */
	private String baseGroupId;

	/**
	 * リクエストされたリアルLoID
	 */
	private String realLoID;

	/**
	 * 集計結果格納クラス
	 */
	LoLogGroupCompareResult result = new LoLogGroupCompareResult();

	/**
	 * コンストラクタ。<br>
	 */
	public LoLogGroupCompareLogic() {
	}

	/**
	 * コンストラクタ。<br>
	 * 
	 * @param groupList 検索画面で選択したグループIDのリスト
	 * @param LoID LOID
	 * @param baseGroupId リクエストされたグループID
	 */
	public LoLogGroupCompareLogic(
		List groupList,
		String LoID,
		String baseGroupId,
		String realLoID)
		throws XmsException {

		// パラメータチェック
		if (groupList == null
			|| LoID == null
			|| baseGroupId == null
			|| realLoID == null) {
			throw new XmsInnerException("parameter is null.");
		}

		this.groupList = groupList;
		this.LoID = LoID;
		this.baseGroupId = baseGroupId;
		this.realLoID = realLoID;
	}

	/**
	 * 集計ロジック・メソッド。<br>
	 * 検索画面より選択されたグループ数回分、<br>、
	 * 配下のログデータを取得し集計します。<br>
	 * 
	 * @param dataAccess DataAccessオブジェクト
	 * @return result ログデータ
	 * @exception XmsException
	 * @see LoLogGroupCompareResult
	 */
	public LogicResult doLogic(DataAccess dataAccess) throws XmsException {

		// パラメータチェック
		if (dataAccess == null) {
			throw new XmsInnerException("parameter is null.");
		}

		List groupIdList;

		try {
			GroupAndPeopleClient GapClient = new GroupAndPeopleClient();
			// ログ共通用Commonクラスの生成
			LoLogCommon_inc cominc = new LoLogCommon_inc();

			// グループIDをキー、順番を値としたハッシュマップの生成
			HashMap hmGroupId = new HashMap();

			// 検索結果画面より選択されたグループの数だけループします。
			// 選択されたグループ配下のグループIDを取得し、
			// その配下の学習ログを集計後、
			// Resultクラスに集計結果を保持します。

			for (int i = 0; i < groupList.size(); i++) {

				groupIdList = new ArrayList();

				// 選択されたグループIDを取得
				String strGroupId = (String) groupList.get(i);

				// グループ名とグループ外部ID取得
				String[] strGroups = cominc.getGroupDetails(strGroupId);

				// グループIDをキー、順序を値としたハッシュに設定する
				hmGroupId.put(strGroupId,Integer.toString(i));

				// GroupId配下のユーザ数を取得
				int intUserCount = GapClient.getUserCount(strGroupId, 0);
				
				// GroupId配下のグループIDを取得
				groupIdList = GapClient.getGroupIdList(strGroupId, groupIdList);

				// ログ集計結果を格納する。
				Object[] logList = null;

				// ＳＱＬ発行回数を取得
				int logSqlCount = cominc.getSqlCount(groupIdList);

				// グループが1000以上の場合は複数回ループ
				// 最低１度はループする。
				for (int j = 0; j < logSqlCount; j++) {
					// グループ配下のログ集計
					logList =
						logSum(realLoID, groupIdList, dataAccess, logList, j);
				}

				// 指定したグループ配下のパスを取得(グループ名でのパス)
				String strGroupNamePath = cominc.getGroupNamePath(strGroupId);

				// グループ配下単位の集計結果をresultクラスに設定する。
				result.addLog(
					logList,
					intUserCount,
					strGroupId,
					strGroups,
					strGroupNamePath);
			}

			// グループ外部ID順に並び替える為に、グループIDをキー、順序を値をして所持する。
			setSortGroupForeignId(hmGroupId);

		} catch (XmsException e) {
			throw e;
		} catch (Exception e) {
			throw new XmsSystemException(e);
		}

		return result;
	}

	private void setSortGroupForeignId(HashMap hmGroupForeign) {

		// グループ外部IDをキー、順序を値としたハッシュをresultに設定する
		result.setHmGroupForeignId(hmGroupForeign);
		
		// グループ外部IDリストを取得
		List sortGroupForeignIdList = new ArrayList();
		sortGroupForeignIdList = result.getGroupForeignIdList();
		// データクラスに設定していたグループIDの外部IDをソートする
		Collections.sort(sortGroupForeignIdList, new GroupIDComparator());
		// ソートしたグループ外部IDリストをresultに設定する
		result.setSortGroupForeignIdList(sortGroupForeignIdList);

	}

	/**
	 * 
	 * グループ外部ＩＤのソート(昇順)クラス
	 * 
	 * @param  比較対象の最初のオブジェクト
	 *          比較対象の 2 番目のオブジェクト
	 *
	 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数。<br>
	 * 　　　　　両方が等しい場合は 0<br>
	 *          最初の引数が 2 番目の引数より大きい場合は正の整数。
	 * 
	 * 
	 */
	class GroupIDComparator implements Comparator {

		public int compare(Object arg0, Object arg1) {

			return compare((List)arg0, (List)arg1);
		}


		public int compare(List groupList0, List groupList1) {

			String groupForeignID0 = null;
			String groupForeignID1 = null;
		
			groupForeignID0 = (String)groupList0.get(1);
			groupForeignID1 = (String)groupList1.get(1);

			return groupForeignID0.compareTo(groupForeignID1);
		}
	}

	/**
	 * グループ配下ログデータ取得メソッド<br>
	 * グループ配下のログデータをデータベースより取得します。<br>
	 * 
	 * @param  strLOID        LOID
	 * @param  lstGroupIdList グループIDのリスト
	 * @param  dataAccess     XMO DAOオブジェクト
	 * @param  logList        ログデータ
	 * @return logList        ログデータ
	 * @exception XmsException
	 */

	private Object[] logSum(
		String strLOID,
		List lstGroupIdList,
		DataAccess dataAccess,
		Object[] logList,
		int sqlCounter)
		throws XmsException {

		ResultSet rs = null;
		boolean exceptionFlag = false;

		try {

			// ループ開始カウント
			int intStart = sqlCounter * 1000;
			// ループ終了カウント
			int intEnd = intStart + 1000;
			// ループ終了カウントよりグループリストサイズの方が小さい場合
			if (lstGroupIdList.size() < intEnd) {
				intEnd = lstGroupIdList.size() - intStart;
			}

			StringBuffer sbSQL = new StringBuffer();

			sbSQL.append("	SELECT ");
			sbSQL.append("	    MIN(LOLog.study_start_date) AS start_date, ");
			// 学習開始日
			sbSQL.append("	    MAX(LOLog.last_access) AS last_access, ");
			// 最終アクセス日
			sbSQL.append("	    SUM(LOLog.Access_cnt) AS access_cnt, ");

			//XMS.iniファイルロードクラスのインスタンス
			Environment objEnv=new Environment();
			//PostgreSQLの場合
			if(objEnv.getDBType().equals( objEnv.DB_POSTGRE )){
				sbSQL.append("	    ROUND(TO_NUMBER(SUM(LOLog.residanceTime)/60000,'99999999999999D99999999999999'),1) AS sum_residance, ");
			//SQLServerの場合 OR Oracleの場合
			}else if(objEnv.getDBType().equals( objEnv.DB_SQLSERVER ) || objEnv.getDBType().equals( objEnv.DB_ORACLE )){
				sbSQL.append("	    ROUND(SUM(LOLog.residanceTime)/60000,1) AS sum_residance, ");
			}

			// 総累積滞在時間
			sbSQL.append("	    COUNT(LOLog.study_end_date) AS end_count, ");
			// 修了者数
			sbSQL.append("	    COUNT(LOLog.passday) AS pass_count ");
			// 合格者数
			sbSQL.append("	FROM ");
			sbSQL.append("	    LOLog INNER JOIN ");
			sbSQL.append("          GroupAndPeople ON LOLog.UserID = GroupAndPeople.childID ");
			sbSQL.append("	WHERE ");
			sbSQL.append("	    (LOLog.LOID = '" + StringEscapeUtils.escapeSql(strLOID) + "') AND ");
			sbSQL.append("     GroupAndPeople.parentID IN ");

			// groupIdを ｢,｣ 区切りの文字列にする
			for (int i = intStart; i < intEnd; i++) {
				String tempGroupId = (String) lstGroupIdList.get(i);
				if (i == 0) {
					sbSQL.append("('" + StringEscapeUtils.escapeSql(tempGroupId) + "'");
				} else {
					sbSQL.append(",'" + StringEscapeUtils.escapeSql(tempGroupId) + "'");
				}
			}
			sbSQL.append(")");

			DebugLog.write( this.getClass(),sbSQL.toString(), DebugLog.ROW );
			// クエリーの実行
			rs = dataAccess.executeQuery(sbSQL.toString());

			// １レコードのみ取得
			if (rs.next()) {
				// SQLのループ１回目の場合		
				if (logList == null) {
					logList = new Object[6];
					// 学習開始日時を取得
					logList[0] = rs.getTimestamp("start_date");
					// 最終アクセス日時を取得
					logList[1] = rs.getTimestamp("last_access");
					// アクセス回数を取得
					logList[2] = rs.getString("access_cnt");
					// 累積滞在時間を取得
					logList[3] = rs.getString("sum_residance");
					// 完了者数を取得
					logList[4] = Integer.toString(rs.getInt("end_count"));
					// 合格者数を取得
					logList[5] = Integer.toString(rs.getInt("pass_count"));

					// SQLのループ２回目以降の場合
				} else {
					// 学習開始日時のログがまだない場合
					if (logList[0] == null) {
						logList[0] = rs.getTimestamp("start_date");
					// ログがあり、DBより取得したデータの方が古い場合は
					// DBからのデータを設定する
					} else if (
						((Date) logList[0]).after(
							rs.getTimestamp("start_date"))) {
						logList[0] = rs.getTimestamp("start_date");
					}

					// 最終アクセス日時のログがまだない場合
					if (logList[1] == null) {
						logList[1] = rs.getTimestamp("last_access");
					// ログがあり、DBより取得したデータの方が新しい場合は
					// DBからのデータを設定する	
					} else if (
						((Date) logList[1]).before(
							rs.getTimestamp("last_access"))) {
						logList[1] = rs.getTimestamp("last_access");

					}
					// アクセス回数を合計する(結果がnullの場合は何もしない)
					// nullでない場合
					if (rs.getString("access_cnt") != null) {
						// 今までのアクセス回数データがnullの場合
						if (logList[2] == null) {
							logList[2] = rs.getString("access_cnt");
						// アクセス回数データがある場合
						} else {
							logList[2] =
								Integer.toString(
									Integer.parseInt(logList[2].toString())
										+ Integer.parseInt(
											rs.getString("access_cnt")));
						}
					}

					// 累積滞在時間を合計する(結果がnullの場合は何もしない)
					if (rs.getString("sum_residance") != null) {
						// 今までの累積滞在時間データがnullの場合
						if (logList[3] == null) {
							logList[3] = rs.getString("sum_residance");
						// 累積滞在時間データがある場合
						} else {
							logList[3] =
								Double.toString(
									Double.parseDouble(logList[3].toString())
										+ Double.parseDouble(rs.getString("sum_residance")));
						}
					}

					// 完了者数を合計する
					logList[4] =
						Integer.toString(
							Integer.parseInt(logList[4].toString())
								+ rs.getInt("end_count"));
					// 合格者数を合計する
					logList[5] =
						Integer.toString(
							Integer.parseInt(logList[5].toString())
								+ rs.getInt("pass_count"));
				}
			}
			exceptionFlag = true;

		} catch (Exception e) {
			throw new XmsSystemException(e);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (Exception e) {
					if (exceptionFlag == true) {
						throw new XmsSystemException(e);
					}
				}
				rs = null;
			}
		}

		return logList;
	}

}
