/*******************************************************************************
 *	All Rights reserved,Copyright (c) K-Opticom
 ********************************************************************************
 *＜プログラム内容＞
 *	システム名		：eo顧客基幹システム
 *	モジュール名	：JCKejbExclusiveProcCK0121
 *	ソースファイル名：JCKejbExclusiveProcCK0121.java
 *	作成者			：富士通
 *	日付			：2011年11月28日
 *＜機能概要＞
 *	お客様グループ設定を起点とするルートの排他処理部品。
 *＜修正履歴＞
 *	バージョン	修正日		修正者		修正内容
 *	ｖ1.00.00	2011/11/28	富士通		新規作成
 *
 ********************************************************************************/

package eo.ejb.common.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import com.fujitsu.futurity.model.base.CAANConnectionMgr;
import com.fujitsu.futurity.model.base.CAANJDBCUtil;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.base.CAANRuntimeException;
import com.fujitsu.futurity.model.ejb.common.JSYejbConnection;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;

import eo.ejb.cbm.entity.CK0011ETMsg;
import eo.ejb.cbm.entity.CK0121ETMsg;
import eo.ejb.cbm.entity.KK1681ETMsg;
import eo.ejb.common.JKKModelCommon;

/**
 * <p>
 * お客様グループ設定を起点とするルートの排他処理部品です。
 * </p>
 * @author 富士通
 *
 */
public class JCKejbExclusiveProcCK0121
{
	/** 更新用キー項目結合用文字（,） */
	private static final String KEY_JOIN_WORD = ",";

	/**
	 * 更新系インターフェイス使用時の処理を行います（グループコード指定）。
	 * レコードのロック、タイムスタンプの実施、ロック対象レコードの更新を行います。
	 * @param inETMsg 処理内で使用する項目を保持しているメッセージ
	 * @param key グループコード
	 * @param updDtmBf タイムスタンプチェック用更新年月日時分秒（更新前）
	 * @param funcCd 機能コード
	 * @return 処理が正常に終了し、タイムスタンプチェックが正常の場合はtrue。
	 */
	public boolean isExProcGrpCd(CAANMsg inETMsg, String key, String updDtmBf, String funcCd)
	{
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JCKejbExclusiveProcKK0121#isExProcGrpCd");

		// グループコードが未設定の場合はtrueを返却する。
		if(null == key)
		{
			return true;
		}

		// 更新年月日時分秒(更新前)が未設定の場合はtrueを返却する。
		if(null == updDtmBf)
		{
			return true;
		}

		//グループコードからSYSIDリストを取得
		ArrayList<String> sysidList = getSysId(key, JKKModelCommon.getOpeDate(inETMsg));

		if(sysidList.size() < 1)
		{
			return true;
		}
		
		// 処理対象のSYSIDリストを取得
		ArrayList<String> tgtList = new ArrayList<String>();

		for(int i = 0; i < sysidList.size(); i++)
		{
			ArrayList<String> sysList = getLockTgt(inETMsg, sysidList.get(i));

			if(sysList.size() != 0)
			{
				// 検索結果をSYSIDリストに反映
				setSys(tgtList, sysList);
			}
		}

		ArrayList<String> updArray = new ArrayList<String>();
		
		ArrayList<String> keyArrayCK0011 = new ArrayList<String>();
		
		// レコードロックの実行
		try 
		{
			for(int i = 0; i < tgtList.size(); i++)
			{
				// 処理対象のSYSIDの最終更新年月日時分秒のレコードを取得
				ArrayList<HashMap<String, String>> resultList = getLastUpdRecordCK0011(tgtList.get(i));
				if(resultList.size() == 0)
				{
					return true;
				}
				
				for(HashMap<String, String> hm : resultList)
				{
					// お客様をロック
					CAANMsg msg = new CAANMsg(CK0011ETMsg.class.getName());
					msg.set(CK0011ETMsg.SYSID, hm.get(CK0011ETMsg.SYSID));
					msg.set(CK0011ETMsg.GENE_ADD_DTM, hm.get(CK0011ETMsg.GENE_ADD_DTM));
					new JKKejbDBAUtil(msg).findByKeyForUpdate(msg);
					
					// キー情報の格納
					StringBuffer primary = new StringBuffer();
					primary.append(hm.get(CK0011ETMsg.SYSID));
					primary.append(KEY_JOIN_WORD);
					primary.append(hm.get(CK0011ETMsg.GENE_ADD_DTM));
					keyArrayCK0011.add(primary.toString());
					
					// 更新年月日時分秒の格納
					updArray.add(hm.get(CK0011ETMsg.UPD_DTM));
				}
			}
		}
		catch (CAANRuntimeException ce)
		{
			// レコードロックに失敗した場合はfalseを返却する
			return false;
		}

		// データが取得できなかった場合は処理を終了する
		if(keyArrayCK0011.size() < 1)
		{
			return true;
		}

		// 共通処理のインスタンスを生成
		JKKejbExclusiveProcCommon epc = new JKKejbExclusiveProcCommon();
		
		// タイムスタンプチェックを行い、チェックエラーの場合はfalseを返却する。
		if (!epc.isTimeStampCheck(updArray, updDtmBf))
		{
			return false;
		}

		// 機能コードが実行モードの場合はロック対象レコード更新処理を行う
		if (!epc.isFuncMode(funcCd))
		{
			epc.updateCK0011(inETMsg, keyArrayCK0011);
		}

		return true;
	}

	/**
	 * 照会系インターフェイス使用時の処理を行います（グループコード指定）。
	 * 指定されたキーに紐付く親テーブルを各ルートから検索し、
	 * その中で最大の更新年月日時分秒を取得します。
	 * @param inMsg 処理対象のメッセージ
	 * @param key グループコード
	 * @return 指定したキーに紐付く最終更新年月日時分秒。
	 */
	public String getLastUpdDtmGrpCd(CAANMsg inMsg, String key)
	{
		// ログの出力
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JCKejbExclusiveProcCK0121#getLastUpdDtmGrpCd");
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JCKejbExclusiveProcCK0121#key=" + key);
		
		//グループコードからSYSIDリストを取得
		ArrayList<String> sysidList = getSysId(key, JKKModelCommon.getOpeDate(inMsg));

		// データが取得できなかった場合はnullを返却
		if(sysidList.size() < 1)
		{
			return null;
		}
		
		// 処理対象のSYSIDリストを取得
		ArrayList<String> tgtList = new ArrayList<String>();

		for (int i = 0; i < sysidList.size(); i++)
		{
			ArrayList<String> sysList = getLockTgt(inMsg, sysidList.get(i));

			if(sysList.size() != 0)
			{
				// 検索結果をSYSIDリストに反映
				setSys(tgtList, sysList);
			}
		}
		
		ArrayList<String> updArray = new ArrayList<String>();
		
		for (int i = 0; i < tgtList.size(); i++)
		{
			ArrayList<HashMap<String, String>> resultList = getLastUpdRecordCK0011(tgtList.get(i));
			for(HashMap<String, String> hm : resultList)
			{
				updArray.add(hm.get(CK0011ETMsg.UPD_DTM));
			}
		}

		// データが取得できなかった場合はエラーとする
		if(updArray.size() < 1)
		{
			return null;
		}

		// 最終更新年月日時分秒を取得する
		String maxUpdDtm = new JKKejbExclusiveProcCommon().getLastUpdDtm(updArray);

		return maxUpdDtm;
	}

	/**
	 * グループコードに紐付く、SYSIDを取得します。
	 * @param keyGrpId グループコード
	 * @param opeDate 運用日付
	 * @return データ取得用のSQL文。
	 */
	private ArrayList<String> getSysId(String keyGrpId, String opeDate)
	{
		
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();
		

		sbSql.append("SELECT ");
		sbSql.append("    CK0121.SYSID ");
		sbSql.append("FROM ");
		sbSql.append("    CK_T_CUST_GRP_SETTE CK0121 ");
		sbSql.append("WHERE ");
		sbSql.append("    CK0121.GRP_CD = ? ");
		sbSql.append("AND (CK0121.CUST_GRP_SETTE_NO, CK0121.RSV_APLY_YMD || CK0121.GENE_ADD_DTM) = ");
		sbSql.append("    (SELECT  CK0121_GENE.CUST_GRP_SETTE_NO, MAX(CK0121_GENE.RSV_APLY_YMD || CK0121_GENE.GENE_ADD_DTM) AS CK0121_MAX ");
		sbSql.append("     FROM    CK_T_CUST_GRP_SETTE CK0121_GENE ");
		sbSql.append("     WHERE   CK0121_GENE.CUST_GRP_SETTE_NO = CK0121.CUST_GRP_SETTE_NO ");
		sbSql.append("     AND     CK0121_GENE.RSV_APLY_YMD <= ? ");
		sbSql.append("     AND     CK0121_GENE.RSV_APLY_CD = '2' ");
		sbSql.append("     AND     CK0121_GENE.MK_FLG = '0'  ");
		sbSql.append("     GROUP BY CK0121_GENE.CUST_GRP_SETTE_NO ) ");
		sbSql.append("GROUP BY CK0121.SYSID ");
		

		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(CK0121ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, keyGrpId);
			CAANJDBCUtil.setParam(pstmt, 2, opeDate);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 返却領域の生成
			ArrayList<String> rsltList = new ArrayList<String>();
			
			while (rsltQuery.next())
			{
				// 結果をリストに格納
				rsltList.add(rsltQuery.getString(1));
			}

			// 取得した情報を返却
			return rsltList;

		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
		
	}
	
	/**
	 * ロック対象キーの取得を行います。
	 * @param inMsg 処理対象のメッセージ
	 * @param key SYSID
	 * @return ロック対象のSYSIDリスト
	 */
	private ArrayList<String> getLockTgt(CAANMsg inMsg, String key)
	{
		ArrayList<String> retList = new ArrayList<String>();
		
		// keyを結果リストに詰める
		retList.add(key); 

		for (int i = 0; i < retList.size(); i++)	
		{	
			// ロック対象リストに設定されたSYSIDに異動予約の以下の項目に対して検索を行う。
			// @SYSIDに対する検索
			// A併合先SYSIDに対する検索
			// B分割先SYSIDに対する検索
			ArrayList<HashMap<String, String>> resultList = searchSysid(retList.get(i), JKKModelCommon.getOpeDate(inMsg));
			
			if(resultList.size() != 0)
			{
				// 検索結果をロック対象リストに反映
				setList(retList, resultList);
			}
		}
		
		return retList;
	}
	
	/**
	 * 検索結果の値をリストに追加する。
	 * @param keyLst ロック対象リスト
	 * @param tgtLst 検索結果
	 */
	private void setList(ArrayList<String> keyLst, ArrayList<HashMap<String, String>> tgtLst)
	{
		
		// 設定済みSYSID保持用
		HashMap<String, String> hash = new HashMap<String, String>();
		
		for (int k = 0; k < keyLst.size(); k++)
		{
			// 現在のkey情報の詰め替え
			hash.put(keyLst.get(k), keyLst.get(k));
		}
		
		for (int i = 0; i < tgtLst.size(); i++)
		{
			String tgtSysid = tgtLst.get(i).get(KK1681ETMsg.SYSID);
			String tgtHgskSysid = tgtLst.get(i).get(KK1681ETMsg.HEIGO_SK_SYSID);
			String tgtBkskSysid = tgtLst.get(i).get(KK1681ETMsg.BUNKATSU_SK_SYSID);
			
			// 検索結果のkeyLst内の存在判定を行う
			// SYSID
			if(tgtSysid != null && !"".equals(tgtSysid))
			{
				if(!hash.containsKey(tgtSysid))
				{
					// 一致する値が存在しない場合にkeyLstに追加する
					hash.put(tgtSysid, tgtSysid);
					keyLst.add(tgtSysid);
				}
			}
			
			// 併合先SYSID
			if(tgtHgskSysid != null && !"".equals(tgtHgskSysid))
			{
				if(!hash.containsKey(tgtHgskSysid))
				{
					hash.put(tgtHgskSysid, tgtHgskSysid);
					keyLst.add(tgtHgskSysid);
				}
			}
			
			// 分割先SYSID
			if(tgtBkskSysid != null && !"".equals(tgtBkskSysid))
			{
				if(!hash.containsKey(tgtBkskSysid))
				{
					hash.put(tgtBkskSysid, tgtBkskSysid);
					keyLst.add(tgtBkskSysid);
				}
			}		
		}
	}

	/**
	 * 対象SYSIDの値をリストに追加する。
	 * @param sLst SYSIDリスト
	 * @param schLst 検索結果
	 */
	private void setSys(ArrayList<String> sLst, ArrayList<String> schLst)
	{
		
		// 設定済みSYSID保持用
		HashMap<String, String> hash = new HashMap<String, String>();
		
		for (int j = 0; j < sLst.size(); j++)
		{
			// 現在のkey情報の詰め替え
			hash.put(sLst.get(j), sLst.get(j));
		}
		
		for (int i = 0; i < schLst.size(); i++)
		{
			
			// 検索結果のsLst内の存在判定を行う
			if(schLst.get(i) != null && !"".equals(schLst.get(i)))
			{
				if(!hash.containsKey(schLst.get(i)))
				{
					// 一致する値が存在しない場合にsLstに追加する
					hash.put(schLst.get(i), schLst.get(i));
					sLst.add(schLst.get(i));
				}
			}
			
		}
	}

	/**
	 * 異動予約に対するSYSIDの検索を行う。
	 * @param key SYSID
	 * @param opeDate 運用日時
	 * @return 検索結果
	 */
	private ArrayList<HashMap<String, String>> searchSysid(String key, String opeDate)
	{
		
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();
		
		sbSql.append(" SELECT ");
		sbSql.append("    NULL SYSID, ");
		sbSql.append("    KK1681.HEIGO_SK_SYSID, ");
		sbSql.append("    KK1681.BUNKATSU_SK_SYSID ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.SYSID = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");
		sbSql.append(" UNION ");
		sbSql.append(" SELECT ");
		sbSql.append("    KK1681.SYSID, ");
		sbSql.append("    NULL HEIGO_SK_SYSID, ");
		sbSql.append("    NULL BUNKATSU_SK_SYSID ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.HEIGO_SK_SYSID = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");
		sbSql.append(" UNION ");
		sbSql.append(" SELECT ");
		sbSql.append("    KK1681.SYSID, ");
		sbSql.append("    NULL HEIGO_SK_SYSID, ");
		sbSql.append("    NULL BUNKATSU_SK_SYSID ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.BUNKATSU_SK_SYSID = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");

		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(KK1681ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, key);
			CAANJDBCUtil.setParam(pstmt, 2, opeDate);
			
			CAANJDBCUtil.setParam(pstmt, 3, key);
			CAANJDBCUtil.setParam(pstmt, 4, opeDate);
			
			CAANJDBCUtil.setParam(pstmt, 5, key);
			CAANJDBCUtil.setParam(pstmt, 6, opeDate);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 格納マップの生成
			ArrayList<HashMap<String, String>> rsltList = new ArrayList<HashMap<String, String>>();
			
			// 取得結果をマップに格納
			while (rsltQuery.next())
			{
				// 格納マップの生成
				HashMap<String, String> mapRslt = new HashMap<String, String>();
				mapRslt.put(KK1681ETMsg.SYSID, rsltQuery.getString(1));
				mapRslt.put(KK1681ETMsg.HEIGO_SK_SYSID, rsltQuery.getString(2));
				mapRslt.put(KK1681ETMsg.BUNKATSU_SK_SYSID, rsltQuery.getString(3));

				rsltList.add(mapRslt);
			}

			// 取得した情報を返却
			return rsltList;

		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
		
	}

	/**
	 * <p>
	 * パラメータで渡されたSYSIDに該当する
	 * お客様テーブル、異動予約テーブルで最後に更新されたレコードを取得する。
	 * </p>
	 * @param  primaryKey SYSID
	 * @return rsltList   検索結果
	 */
	public ArrayList<HashMap<String, String>> getLastUpdRecordCK0011(String primaryKey)
	{
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();

		sbSql.append(" SELECT ");
		sbSql.append("    CK0011.SYSID, ");
		sbSql.append("    CK0011.GENE_ADD_DTM, ");
		sbSql.append("    CK0011.UPD_DTM ");
		sbSql.append(" FROM ");
		sbSql.append("    CK_T_CUST CK0011 ");
		sbSql.append(" WHERE   CK0011.SYSID = ? ");
		sbSql.append(" AND     (CK0011.SYSID, CK0011.UPD_DTM) =  ");
		sbSql.append("          (SELECT CK0011_01.SYSID, MAX(CK0011_01.UPD_DTM) AS CK0011_MAX ");
		sbSql.append("           FROM   CK_T_CUST CK0011_01 ");
		sbSql.append("           WHERE  CK0011_01.SYSID = CK0011.SYSID ");
		sbSql.append("           GROUP BY CK0011_01.SYSID ) ");
		
		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(CK0011ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, primaryKey);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 格納マップの生成
			ArrayList<HashMap<String, String>> rsltList = new ArrayList<HashMap<String, String>>();
			
			// 取得結果をマップに格納
			while (rsltQuery.next())
			{
				// 格納マップの生成
				HashMap<String, String> mapRslt = new HashMap<String, String>();

				//お客様の情報を返却
				mapRslt.put(CK0011ETMsg.SYSID, rsltQuery.getString(1));
				mapRslt.put(CK0011ETMsg.GENE_ADD_DTM, rsltQuery.getString(2));
				mapRslt.put(CK0011ETMsg.UPD_DTM, rsltQuery.getString(3));
				rsltList.add(mapRslt);
			}

			// 取得した情報を返却
			return rsltList;

		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
	}
	
}
