/////////////////////////////////////////////////////////////////
//Copyright (C) 2005 NTT CORPORATION.
//
//LoInfo オブジェクトリスト生成クラス
//
//変更履歴
//2005.02.03  新規作成  森　智志
//
//機能
//
//条件に従い、LoInfo オブジェクトのリストを生成します。
//
/////////////////////////////////////////////////////////////////
package jp.co.ntt.lms.lo.lom.learnableinfo;

import java.io.File;
import java.util.*;
import java.util.regex.*;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.lo.scorm.*;
import jp.co.ntt.lms.xmo.Environment;
import jp.co.ntt.lms.xmo.Lo.*;
import jp.co.ntt.lms.xmo.GroupAndPeople.GroupAndPeopleClient;
import jp.co.ntt.lms.xmo.Roles.RolesClient;
import jp.co.ntt.lms.xmo.RoleAssignments.RoleAssignmentsClient;
import jp.co.ntt.lms.xmo.Assignments.AssignmentsClient;
import jp.co.ntt.lms.xmo.LoRelation.*;
import jp.co.ntt.lms.xmo.People.PeopleClient;

/**
 * 条件に従い、LoInfo オブジェクトのリストを生成するクラスです。
 * @author s-mori
 * @version 1.0
 */
public class LoInfoFactory {

	/** ユーザアサイン管理ロールID */
	private static final String FUNCTION_LO_ASSIGNUSER = "9";

	/** LOアサイン管理ロールID */
	private static final String FUNCTION_LO_ASSIGNLO = "13";

	/** ルートグループのグループID */
	private static final String GROUP_ID_ROOT = "GR000000";

	/** ルートユーザのユーザID */
	private static final String USER_ID_ROOT = "PE000000";

	/** コンテナのLOタイプ */
	private static final String LO_TYPE_CONTAINER = "0";

	/** ルートコンテナのLOID */
	private static final String LO_ID_ROOT = "LO0";
	
	
	/**
	 * コンストラクタ。
	 * インスタンスを生成します。
	 */
	public LoInfoFactory(){
	}
	
	/**
	 * LO管理権限のあるロールを取得します。
	 * 全ロールレコードを取得し、function = 9,13 のみ取り出す
	 * @return LO管理権限のあるロールIDのリスト
	 * @throws Exception 
	 */
	private List getLoManagableRoleIdList() throws Exception{
	
		RolesClient rlClient = new RolesClient();
		rlClient.select();
		
		List loManageRoleId = new ArrayList();
		while(rlClient.next()){
			for(int i = 0; i < rlClient.getFunctionCount(); i++){
				if(rlClient.getFunction(i).equals(FUNCTION_LO_ASSIGNUSER) || rlClient.getFunction(i).equals(FUNCTION_LO_ASSIGNLO)){
					loManageRoleId.add(rlClient.getRoleID());
					break;
				}
			}
		}
		
		return loManageRoleId;
	}
	
	/**
	 * RoleAssignment テーブルから、ユーザが属するグループに対してLO管理ロールを持つユーザを取得します。
	 * @param parentGroupId 親グループ（ユーザが属するグループ）のID
	 * @param loManagableRoleIdList LO管理権限のあるロールIDのリスト
	 * @return 親グループ対してLO管理ロールを持つユーザIDのリスト
	 * @throws Exception
	 */
	private List getManagableUserIdList(String parentGroupId, List loManagableRoleIdList) throws Exception{
		if(parentGroupId == null || parentGroupId.equals("")){
			throw new NullPointerException("Parent group ID is null.");
		}
		if(loManagableRoleIdList == null){
			throw new NullPointerException("Managable role ID list is null.");
		}
		
		RoleAssignmentsClient raClient = new RoleAssignmentsClient();
		StringBuffer sbuf = new StringBuffer("");
		for(int i = 0; i < loManagableRoleIdList.size(); i++){
			sbuf.append(",'");
			sbuf.append(StringEscapeUtils.escapeSql((String)loManagableRoleIdList.get(i)));
			sbuf.append("'");
		}
		raClient.setCondition("groupid = '" + StringEscapeUtils.escapeSql(parentGroupId) + "' and role_id in (" + sbuf.substring(1) + ") and user_id <> '" + USER_ID_ROOT + "'");
		
		raClient.select();
		
		List managableUserIdList = new ArrayList();
		while(raClient.next()){
			// 取得したユーザIDが削除ユーザでなければ、リストに追加
			if(this.isValidUser(raClient.getUserID())){				
				managableUserIdList.add(raClient.getUserID());
			}
		}
		
		return managableUserIdList;
	}
	
	
	/**
	 * ユーザIDの親のグループIDを取得します。
	 * @param userId ユーザID
	 * @return ユーザが属するグループのID
	 * @throws Exception
	 */
	private String getParentGroupId(String userId) throws Exception{
		if(userId == null || userId.equals("")){
			throw new NullPointerException("User ID is null.");
		}		
		
		// GroupAndPeople テーブルから、自分の親のグループを取得
		GroupAndPeopleClient gpClient = new GroupAndPeopleClient();
		gpClient.setWhereChildID(userId);
		gpClient.select();
		if(gpClient.next()){
			return gpClient.getParentID();
		}else{
			return null;
		}
		
	}

	/**
	 * Assignments テーブルから、LO管理ロールを持つユーザが管理可能な materialID を取得します。
	 * @param managableUserIdList LO管理ロールを持つユーザIDのリスト
	 * @return ユーザがい管理可能な materialID のリスト
	 * @throws Exception
	 */
	private List getManagableMaterialIdList(List managableUserIdList) throws Exception{
		if(managableUserIdList == null){
			throw new NullPointerException("Managable user ID list is null.");
		}
		
		List materialIdList = new ArrayList();
		
		AssignmentsClient asClient = new AssignmentsClient();
		for(int i = 0; i < managableUserIdList.size(); i++){
			asClient.setCondition("membershipid like '%" + StringEscapeUtils.escapeSql((String)managableUserIdList.get(i)) + "%' and management_authority = '1'");
			asClient.select();
			while(asClient.next()){
				materialIdList.add(asClient.getMaterialID().trim());
			}
			asClient.clear();
		}
		
		return materialIdList;
		
	}
	
	

	
	/**
	 * parentLoId 以下の LO を LoInfo のリストとして取得します。
	 * このメソッドは、内部で再帰呼び出しされ、子孫のLOをすべて取得します。
	 * @param parentLoId 親のLOID
	 * @return 子孫のLOの LoInfo オブジェクトのリスト
	 * @throws Exception
	 */
	private List getDescendantLoInfoList(String parentLoId) throws Exception{
		if(parentLoId == null || parentLoId.equals("")){
			throw new NullPointerException("Parent LOID is null.");
		}
		
		List list = new ArrayList();
		
		// LoRelationDataオブジェクトを作成
		LoRelationClient lrClient = new LoRelationClient();
		lrClient.setWhereParentID(parentLoId);
		lrClient.select();
		while(lrClient.next()){

			LoInfo loInfo = this.getLoInfo(lrClient.getChildID());
			
			// リストに追加
			list.add(loInfo);
		}
		
		// ここで、リストの各メンバに対して再帰をかける
		for(int i = 0; i < list.size(); i++){
			LoInfo info = (LoInfo)list.get(i);
			List childList = this.getDescendantLoInfoList(info.getRelationData().getChildID());
			list.addAll(childList);
		}
		
		
		return list;
	}
	
		
	/**
	 * LoInfo オブジェクトを作成します。
	 * @param loId LOID
	 * @return LOID に対応した LoInfo オブジェクト
	 * @throws Exception
	 */
	private LoInfo getLoInfo(String loId) throws Exception{
		if(loId == null || loId.equals("")){
			throw new NullPointerException("LOID is null.");
		}

		LoInfo loInfo = new LoInfo();
		
		// Loオブジェクトを作成
		LoClient loClient = new LoClient();
		loClient.setWhereLoID(loId);
		loClient.select();
		if(loClient.next()){
			// 削除フラグが立っていたら、nullを戻す
			if(loClient.getDeleteFlag().equals("1")){
				return null;
			}
			loInfo.setLoData(loClient.getLoData());
		}else{
			return null;
		}

		// LOExtendオブジェクトを作成
		loClient = new LoClient();
		loClient.selectLoExtend(loId);
		if(loClient.isExistLoExtend()){
			loInfo.setLoExtendRecord(loClient.getLoExtendRecord());
		}
		
		// LoRelationDataオブジェクトを作成
		LoRelationClient lrClient = new LoRelationClient();
		lrClient.setWhereChildID(loId);
		lrClient.select();
		if(lrClient.next()){
			
			LoRelationData lrData = new LoRelationData();
			lrData.setMaterialID(lrClient.getmaterialID());
			lrData.setParentID(lrClient.getParentID());
			lrData.setChildID(lrClient.getChildID());

			loInfo.setRelationData(lrData);
			
		}

		// manifestList オブジェクト作成
		loInfo.setManifestList(this.getManifestList(loId));

		return loInfo;

	}
	

	/**
	 * ManifestList オブジェクトを生成します。
	 * @param loID LOID
	 * @return LOID に対応したManifestListオブジェクト
	 * @throws Exception
	 */
	private ManifestList getManifestList(String loID) throws Exception {

		// SCORMLO テーブルから materialID を取得
		String materialID = null;
		Environment environment = new Environment();
		String serverName = environment.getLoServerName();
		ScormLoClient slClient = new ScormLoClient(serverName);
				
		slClient.getData(loID);
		if(slClient.getDataLength() == 0){
			return null;
		}
		
		ScormLoData loData = (ScormLoData)slClient.getDataInfo(0);

		// 教材IDの取得
		materialID = loData.getMaterialID();
		
		// scorm.ini 読込
		ScormLoEnv scormEnv = new ScormLoEnv();
		
		// manifest が登録されているパス(material_path)を取得
		File manifestDirectory = new File(scormEnv.getMaterialPath() + File.separator + materialID);

		// 
		ManifestListFactory factory = new ManifestListFactory();
		return factory.makeManifestList(manifestDirectory);
		
	}

	/**
	 * 指定したユーザIDが有効なユーザかどうかを判別します。
	 * 有効なユーザとは、DBに存在し、削除フラグが立っていないユーザを指します。
	 * @param userId ユーザID
	 * @return 有効なユーザである場合はtrue
	 * @throws Exception
	 */
	private boolean isValidUser(String userId) throws Exception{
		if(userId == null || userId.equals("")){
			throw new NullPointerException("User ID is null.");
		}
		PeopleClient pClient = new PeopleClient();
		pClient.setWhereUserID(userId);
		pClient.select();
		return(pClient.next() && pClient.getDeleteFlag().equals("0"));
		
	}
	
	
	/**
	 * 管理可能ユーザID一覧から、それらの人々が管理可能なLOのリストを取得します。
	 * @param managableUserIdList LO管理権限を持つユーザのリスト
	 * @return 各ユーザが管理可能なLOの、LoInfoオブジェクトのリスト
	 * @throws Exception
	 */
	private List getLoInfoList(List managableUserIdList) throws Exception{
		
		// assignments テーブルから、LO管理ロールを持つユーザが管理可能な materialID を取得
		List managableMaterialIdList = this.getManagableMaterialIdList(managableUserIdList);
		
		// materialID 以下の LO を LoInfo のリストとして取得
		List loInfoList = new ArrayList();
		for(int i = 0; i < managableMaterialIdList.size(); i++){
			
			// ChildID を取得
			String materialId = (String)managableMaterialIdList.get(i);
			Pattern pattern = Pattern.compile("^LO[0-9]+");
			Matcher matcher = pattern.matcher(materialId);
			String childId = matcher.replaceFirst("");
			
			// 自分自身が「ルートコンテナでない」かつ「ルート直下のコンテナでない」場合は、
			// 自分自身の LoInfo を作成 (rootの場合は何でもok)
			LoInfo myLoInfo = this.getLoInfo(childId);
			if(!myLoInfo.getRelationData().getParentID().equals(LO_ID_ROOT) || !myLoInfo.getLoData().getLoType().equals(LO_TYPE_CONTAINER)){
				loInfoList.add(myLoInfo);
			}
			
			
			loInfoList.addAll(this.getDescendantLoInfoList(childId));
		}

		// 重複をはじく
		Map htTemp = new Hashtable();
		List mergedLoInfoList = new ArrayList();
		for(int i = 0; i < loInfoList.size(); i++){
			// LoInfo を取得
			LoInfo tempInfo = (LoInfo)loInfoList.get(i);

			// 新出の LoInfo なら、リストに追加
			if(htTemp.get(tempInfo.getLoData().getLoID()) == null){
				htTemp.put(tempInfo.getLoData().getLoID(), "1");
				mergedLoInfoList.add(tempInfo);
			}
			
		}
		
		return mergedLoInfoList;
	}
	
	
	/**
	 * 指定されたLO管理ユーザが管理可能な LO 情報を、LoInfo オブジェクトの List 形式で取得します。
	 * @param managingUserId 管理者のユーザID
	 * @return 管理者が管理可能な LOの、LoInfoオブジェクトのリスト
	 * @throws Exception
	 */
	public List getManagableLoInfoList(String managingUserId) throws Exception{
		if(managingUserId == null || managingUserId.equals("")){
			throw new NullPointerException("Manager user ID is null.");
		}

		// ユーザIDの有効性をチェック
		if(!this.isValidUser(managingUserId)){
			throw new Exception("Invalid manager user ID.");
		}

		// rootの場合は、すべてのLoに対するLoInfoを作成
		if(managingUserId.equals(USER_ID_ROOT)){
			List loInfoList = new ArrayList();
			LoClient loClient = new LoClient();
			if(loClient.select()){
				while(loClient.next()){
					loInfoList.add(this.getLoInfo(loClient.getLoID()));
				}
			}

			return loInfoList;
		}else{
			
			// パラメータで渡されたユーザIDを、管理可能ユーザID一覧に追加
			List managableUserIdList = new ArrayList();
			managableUserIdList.add(managingUserId);
			
			// 管理可能ユーザID一覧から、それらの人々が管理可能なLOのリストを取得
			List loInfoList = this.getLoInfoList(managableUserIdList);
			return loInfoList;

		}
	}
	
	
	/**
	 * 指定された受講ユーザが受講可能な LO 情報を、LoInfo オブジェクトの List 形式で取得します。
	 * @param learningUserId 学習者のユーザID
	 * @return 学習者が受講可能なLOの、LoInfoオブジェクトのリスト
	 * @throws Exception
	 */
	public List getLearnableLoInfoList(String learningUserId) throws Exception{
		if(learningUserId == null || learningUserId.equals("")){
			throw new NullPointerException("Learner user ID is null.");
		}
		
		// ユーザIDの有効性をチェック
		if(!this.isValidUser(learningUserId)){
			throw new Exception("Invalid learner user ID.");
		}
		
		
		// LO管理権限のあるロールの取得
		List loManagableRoleIdList = this.getLoManagableRoleIdList();

		// 親グループIDを取得
		String parentGroupId = this.getParentGroupId(learningUserId);
		
		// rootの場合は parentGroupId が null となるので、GROUP_ID_ROOT で代用する。
		if(parentGroupId == null){
			parentGroupId = GROUP_ID_ROOT;
		}

		// RoleAssignment テーブルから、ユーザが属するグループに対してLO管理ロールを持つユーザを取得
		List managableUserIdList = this.getManagableUserIdList(parentGroupId, loManagableRoleIdList);

		// 管理可能ユーザID一覧から、それらの人々が管理可能なLOのリストを取得
		List loInfoList = this.getLoInfoList(managableUserIdList);

		return loInfoList;
		
	}
	

	
	
}
