/*********************************************************************
*	All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JKKejbKK0081NumberParts
*	ソースファイル名：JKKejbKK0081NumberParts.java
*	作成者			：富士通
*	日付			：2011年03月14日
*＜機能概要＞
*	サービス契約採番部品クラス
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v1.00.00	2011/03/14	富士通		新規作成
*	v23.00.00	2016/04/18	FJ)藤本		【ANK-2783-00-00】お客さまＩＤ重複障害対応：採番ロジック見直し
*
**********************************************************************/

package eo.ejb.common.edit;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.HashMap;

import com.fujitsu.futurity.common.JCMConstants;
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 com.fujitsu.futurity.model.ejb.common.JSYejbSucceedDataAccess;
import com.fujitsu.futurity.model.ejb.common.fw.AgentDispatchContext;

import eo.common.util.JKKSvcKeiNoSaiban;
import eo.ejb.cbm.entity.KK0081ETMsg;
import eo.ejb.cbm.entity.KK2111ETMsg;
import eo.ejb.cbm.entity.KK2111LE;
import eo.ejb.common.JCCModelCommon;
import eo.ejb.common.JZMModelCommon;
import eo.ejb.common.db.JKKejbDBAUtil;
import eo.ejb.common.db.JKKejbZM0321DBAccess;

/**
 * <p>
 * サービス契約採番部品クラスです。
 * </p>
 * @author 富士通
 */
public class JKKejbKK0081NumberParts extends JKKejbNumberPartsBase
{

	/** 引継データアクセスキー(サービス契約番号) */
	private static final String KEY_SVCKEINO = "SVCKEINO";

	/** サービス契約シーケンス名 */
	private static final String SEQSEIOPSVCKEINO = "SEQ_SVC_KEI_NO";

	/** サービス契約シーケンス名 */
	private static final String SEQSHKDFLTPWD = "SEQ_SHK_DFLT_PWD";

	/** 接頭語(空白) */
	private static final String PREFIX_KUUHAKU = "";

	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL START
//	/** 接頭語(C) */
//	private static final String PREFIX_C = "C";
	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL END

	/** 乱数発生桁数（10桁） */
	private static final int RANDOM_LENGTH = 10;
	
	/** 桁数(8) */
	private static final int KETASUU = 8;

	/** 桁数(10) */
	private static final int KETASUU_10 = 10;

	/** 整理番号用初期採番値 */
	private static final String SEIRINO_DEFAULT_SEQ = "0000";

	/** 整理番号用採番限界値 */
	private static final String SEIRINO_LIMIT_OVER = "9999";

	/** 整理番号シーケンスフォーマット */
	private static final String SEIRI_SEQ_FORMAT = "0000";

	/** 無効フラグ(有効) */
	protected static final String MK_FLG_YK = "0";

	/** 業務パラメータ（サービス契約番号重複チェックリトライ回数） */
	private static final String KEY_KK_SVC_NO_RTRY_CNT = "KK_SVC_NO_RTRY_CNT";

	/** 結果MAPキー（サービス契約番号） */
	private static final String KEY_SVC_KEI_NO = "svc_kei_no";

	/** 結果MAPキー（完了コード） */
	private static final String KEY_FIN_CD = "fin_cd";

	/** 結果MAPキー（詳細コード） */
	private static final String KEY_DTL_CD = "dtl_cd";

	/** 完了コード（採番エラー） */
	private static final String FIN_CD_SAIBAN_ERR = "92";

	/** 詳細コード（サービス契約番号枯渇(シーケンス枯渇)） */
	private static final String DTL_CD_SEQ_OVER = "92001";

	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL START
//	/** 変換表Ａ */
//	private static final HashMap<String, String> CHANGE_LIST_A = new HashMap<String, String>();
//
//	static
//	{
//		// 変換配列設定
//		CHANGE_LIST_A.put("0", "RSUWXZFGHJ");
//		CHANGE_LIST_A.put("1", "SUWXZFGHJR");
//		CHANGE_LIST_A.put("2", "UWXZFGHJRS");
//		CHANGE_LIST_A.put("3", "WXZFGHJRSU");
//		CHANGE_LIST_A.put("4", "XZFGHJRSUW");
//		CHANGE_LIST_A.put("5", "ZFGHJRSUWX");
//		CHANGE_LIST_A.put("6", "FGHJRSUWXZ");
//		CHANGE_LIST_A.put("7", "GHJRSUWXZF");
//		CHANGE_LIST_A.put("8", "HJRSUWXZFG");
//		CHANGE_LIST_A.put("9", "JRSUWXZFGH");
//	}
	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL END

	/** 変換表Ｂ */
	private static final HashMap<String, String> CHANGE_LIST_B = new HashMap<String, String>();

	static
	{
		// 変換配列設定
		CHANGE_LIST_B.put("0", "npqrstuvwx");
		CHANGE_LIST_B.put("1", "kmnpqrstuv");
		CHANGE_LIST_B.put("2", "pqrstuvwxy");
		CHANGE_LIST_B.put("3", "tuvwxyzabc");
		CHANGE_LIST_B.put("4", "ijkmnpqrst");
		CHANGE_LIST_B.put("5", "cdefghijkm");
		CHANGE_LIST_B.put("6", "mnpqrstuvw");
		CHANGE_LIST_B.put("7", "abcdefghij");
		CHANGE_LIST_B.put("8", "stuvwxyzab");
		CHANGE_LIST_B.put("9", "hijkmnpqrs");
	}

	/**
	 * コンストラクタです。
	 */
	public JKKejbKK0081NumberParts()
	{
	}

	/**
	 * <p>
	 * サービス契約番号の採番を行います。
	 * </p>
	 * 
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @return 採番されたサービス契約番号
	 */
	public Object getSvcKeiNo(CAANMsg inMsg, AgentDispatchContext inContext)
	{
		String value = null;

		// 機能コードのチェック（チェックモードの場合は採番しない）
		if (isFuncMode(inMsg))
		{
			return null;
		}

		// 引継データの取得
		value = (String)JSYejbSucceedDataAccess.getSucceedData(KEY_SVCKEINO);

		if (value != null)
		{
			// 引継データが設定されている場合はまま返却
			return value;
		}
		
		// サービス契約番号採番処理
		String rltValue = tranSaiban();

		if(null == rltValue)
		{
			throw new CAANRuntimeException("サービス契約番号重複チェックリトライ回数内で採番できませんでした。");
		}
		
		// 引継データの設定
		JSYejbSucceedDataAccess.setSucceedData(KEY_SVCKEINO, rltValue);

		return rltValue;
	}

	/**
	 * <p>
	 * サービス契約番号の採番を行います（１トランザクション多重採番仕様）。
	 * </p>
	 * 
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @return 採番されたサービス契約番号
	 */
	public Object getSvcKeiNoSingle(CAANMsg inMsg, AgentDispatchContext inContext)
	{
		// 処理結果マップ
		HashMap<String, Object> rsltMap = new HashMap<String, Object>();
		String rltValue = null;

		// 機能コードのチェック（チェックモードの場合は採番しない）
		if (isFuncMode(inMsg))
		{
			return rsltMap;
		}

		// サービス契約番号採番処理
		rltValue = tranSaiban();
		
		if(null != rltValue)
		{
			// 登録処理
			// サービス契約排他制御の登録データ作成
			CAANMsg dbaETMsg = new CAANMsg(KK2111ETMsg.class.getName());
			dbaETMsg.set(KK2111ETMsg.SVC_KEI_NO, rltValue);
			dbaETMsg.set(KK2111ETMsg.LAST_UPD_DTM, inMsg.getString(JCMConstants.OPERATE_DATETIME_KEY));
			dbaETMsg.set(KK2111ETMsg.ADD_DTM, inMsg.getString(JCMConstants.OPERATE_DATETIME_KEY));
			dbaETMsg.set(KK2111ETMsg.ADD_OPEACNT, inMsg.getString(JCMConstants.OPERATOR_ID_KEY));
			dbaETMsg.set(KK2111ETMsg.UPD_DTM, inMsg.getString(JCMConstants.OPERATE_DATETIME_KEY));
			dbaETMsg.set(KK2111ETMsg.UPD_OPEACNT, inMsg.getString(JCMConstants.OPERATOR_ID_KEY));
			dbaETMsg.set(KK2111ETMsg.MK_FLG, MK_FLG_YK);

			// サービス契約排他制御の登録
			JKKejbDBAUtil dba = new JKKejbDBAUtil(inMsg);
			dba.create(dbaETMsg);

			rsltMap.put(KEY_SVC_KEI_NO, rltValue);
			rsltMap.put(KEY_FIN_CD, null);
			rsltMap.put(KEY_DTL_CD, null);
		}
		// 未採番の場合は処理結果マップの設定を行なう
		else
		{
			rsltMap.put(KEY_SVC_KEI_NO, null);
			rsltMap.put(KEY_FIN_CD, FIN_CD_SAIBAN_ERR);
			rsltMap.put(KEY_DTL_CD, DTL_CD_SEQ_OVER);
		}

		return rsltMap;
	}

	/**
	 * <p>
	 * サービス契約の初期デフォルトパスワードを採番する。
	 * </p>
	 * 
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @return 採番された初期デフォルトパスワード
	 */
	public Object getShkDfltPwd(CAANMsg inMsg, AgentDispatchContext inContext)
	{
		// 機能コードのチェック（チェックモードの場合は採番しない）
		if (isFuncMode(inMsg))
		{
			return null;
		}

		// モデル共通アクセッサー部品・Oracleシーケンス取得処理
		Long seq = Long.parseLong(JCCModelCommon.getFormatedNextSeq(SEQSHKDFLTPWD, "", KETASUU_10));

		// シード値を使用して乱数を発生
		String random = getRandomNumberSeed(RANDOM_LENGTH, seq);

		// 変換処理
		StringBuffer pwd = new StringBuffer();

		// (1)乱数の9桁目,10桁目を元に変換表Ｂ変換処理を呼び出す
		pwd.append(toAlphabetB(random.substring(8, 9), random.substring(9, 10)));
		// (2)乱数の1桁目,10桁目の加算値の下1桁目を設定する
		pwd.append(toNumLastDigit(random.substring(0, 1), random.substring(9, 10)));
		// (3)乱数の3桁目,4桁目を元に変換表Ｂ変換処理を呼び出す
		pwd.append(toAlphabetB(random.substring(2, 3), random.substring(3, 4)));
		// (4)乱数の9桁目を設定する
		pwd.append(random.substring(8, 9));
		// (5)乱数の8桁目を設定する
		pwd.append(random.substring(7, 8));
		// (6)乱数の5桁目,6桁目を元に変換表Ｂ変換処理を呼び出す
		pwd.append(toAlphabetB(random.substring(4, 5), random.substring(5, 6)));
		// (7)乱数の2桁目,4桁目の加算値の下1桁目を設定する
		pwd.append(toNumLastDigit(random.substring(1, 2), random.substring(3, 4)));
		// (8)乱数の7桁目,8桁目を元に変換表Ｂ変換処理を呼び出す
		pwd.append(toAlphabetB(random.substring(6, 7), random.substring(7, 8)));

		// パスワードとして返却する
		return pwd.toString();
	}

	/**
	 * 整理番号の採番を行う。
	 * 
	 * @param inCBSMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @param shk_eoid 初期eoID下10桁
	 * @param maxSeiriNo 整理番号
	 * @param count 採番数
	 * @return null：桁あふれ　String：整理番号
	 */
	public String getSeiriNo(CAANMsg inCBSMsg, AgentDispatchContext inContext, String shk_eoid, String maxSeiriNo, int count)
	{
		// 整理番号を生成する
		StringBuffer strBuff = new StringBuffer();

		// 初期eoID10桁
		strBuff.append(shk_eoid);
		
		// 数値部分が計算の結果桁あふれが起こる場合は採番を行わず処理終了
		if(SEIRINO_LIMIT_OVER.equals(maxSeiriNo)) {
			return null;
		}
		
		// 整理番号が取得できなかった場合
		if (maxSeiriNo.equals("0"))
		{
			// 現在の採番値が取得できなかった場合は、採番をデフォルト値(0000)から始める
			maxSeiriNo = SEIRINO_DEFAULT_SEQ;
		}

		// それ以外の場合は数値部分に採番数分加算する
		int retSeiriNo = Integer.parseInt(maxSeiriNo) + count;
		
		// 加算結果が"10000"となる場合
		if(retSeiriNo == 10000) {
			// 処理終了
			return null;
		}

		// 初期eoIDと整理番号を桁埋めした通番を結合した文字列を返却する
		return strBuff.append(new DecimalFormat(SEIRI_SEQ_FORMAT).format(retSeiriNo)).toString();
	}

	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL START
//	/**
//	 * <p>
//	 * 変換表Ａを使用して英字変換を行います。
//	 * </p>
//	 * @param str1 変換元データの１桁目
//	 * @param str2 変換元データの２桁目
//	 * @return 変換した英字。
//	 */
//	private String toAlphabetA(String str1, String str2)
//	{
//		// 変換表Ａを使用して英字変換を行う
//		return toAlphabetTarget(str1, str2, CHANGE_LIST_A);
//	}
	// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し DEL END

	/**
	 * <p>
	 * 変換表Ｂを使用して英字変換を行います。
	 * </p>
	 * @param str1 変換元データの１桁目
	 * @param str2 変換元データの２桁目
	 * @return 変換した英字。
	 */
	private String toAlphabetB(String str1, String str2)
	{
		// 変換表Ｂを使用して英字変換を行う
		return toAlphabetTarget(str1, str2, CHANGE_LIST_B);
	}

	/**
	 * <p>
	 * 指定された採番対象内で最大の整理番号を取得します。
	 * </p>
	 * @param inMsg 処理対象のメッセージキャリア
	 * @param inContext Agentから渡されたAgentDispatchContext
	 * @param eoId 初期eoID 
	 * @return 採番対象内で最大の整理番号
	 * @throws SQLException 
	 */
	public String getMaxSeiriNo(CAANMsg inMsg, AgentDispatchContext inContext, String eoId) 
	{
		// コネクション
		Connection con1 = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// 返却用変数
		String maxSeiriNo = null;

		try
		{
			//コネクション取得
			con1 = JSYejbConnection.getConnection(KK0081ETMsg.getTableName());

			// SQL文
			StringBuffer sql_Buff = new StringBuffer();
			sql_Buff.append(" SELECT  ")
					.append("    NVL(MAX(SUBSTR(KK0081.SEIRI_NO, -4)), 0) AS MAX_SEIRI_NO  ")
					.append(" FROM  ")
					.append("    KK_T_SVC_KEI KK0081  ")
					.append(" WHERE  ")
					.append("    KK0081.SEIRI_NO LIKE ? ")
					.append(JZMModelCommon.ESCAPE_SQL_STRING);

			//prepareStatementにSQL文をセット
			pstmt = con1.prepareStatement(sql_Buff.toString());

			//ログ出力(SQL文の出力)
			JSYejbLog.outlog(inContext, JSYejbLog.DBACCESS, this.getClass(), sql_Buff);

			// パラメータの設定(初期eoID)
			CAANJDBCUtil.setParam(pstmt, 1, JZMModelCommon.replaceLikeValue(eoId) + "%");

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 最大の整理番号を取得する
			if (rsltQuery.next())
			{
				maxSeiriNo = rsltQuery.getString("MAX_SEIRI_NO");
			}

		}
		catch(SQLException se)
		{
			throw new CAANRuntimeException(se);
		}
		finally
		{
			// 資源の解放
			try
			{
				if(rsltQuery != null)
				{
					rsltQuery.close();
				}
				if(pstmt != null)
				{
					pstmt.close();
				}
				if(con1 != null)
				{
					CAANConnectionMgr.getInstance().close(con1);
				}
			}
			catch(SQLException se)
			{
				throw new CAANRuntimeException(se);
			}
		}
		return maxSeiriNo;
	}
	
	/**
	 * <p>
	 * サービス契約番号採番処理
	 * </p>
	 * @return 採番値
	 */
	private String tranSaiban()
	{

		// 業務パラメータからサービス契約番号重複チェックリトライ回数を取得
		String workRtryCnt = new JKKejbZM0321DBAccess().getParamE(KEY_KK_SVC_NO_RTRY_CNT);
		int rtryCnt = 0;

		if (null != workRtryCnt && !"".equals(workRtryCnt))
		{
			rtryCnt = Integer.parseInt(workRtryCnt);

		}

		for (int i = 0; i < rtryCnt; i++)
		{
			
			// 採番処理
			// モデル共通アクセッサー部品・Oracleシーケンス取得処理
			String value = JCCModelCommon.getFormatedNextSeq(SEQSEIOPSVCKEINO, PREFIX_KUUHAKU, KETASUU);

			// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し MOD START
//			// 変換処理
//			StringBuffer buffer = new StringBuffer();
//
//			// (1)接頭語を設定する
//			buffer.append(PREFIX_C);
//			// (2)シーケンスの2桁目,3桁目を元に変換表Ａ変換処理を呼び出す
//			buffer.append(toAlphabetA(value.substring(1, 2), value.substring(2, 3)));
//			// (3)シーケンスの6桁目,7桁目を元に変換表Ａ変換処理を呼び出す
//			buffer.append(toAlphabetA(value.substring(5, 6), value.substring(6, 7)));
//			// (4)シーケンスの5桁目を設定する
//			buffer.append(value.substring(4, 5));
//			// (5)シーケンスの6桁目を設定する
//			buffer.append(value.substring(5, 6));
//			// (6)シーケンスの1桁目,2桁目を元に変換表Ａ変換処理を呼び出す
//			buffer.append(toAlphabetA(value.substring(0, 1), value.substring(1, 2)));
//			// (7)シーケンスの3桁目,4桁目を元に変換表Ａ変換処理を呼び出す
//			buffer.append(toAlphabetA(value.substring(2, 3), value.substring(3, 4)));
//			// (8)シーケンスの7桁目を設定する
//			buffer.append(value.substring(6, 7));
//			// (9)シーケンスの8桁目を設定する
//			buffer.append(value.substring(7, 8));
//			// (10)設定された値からチェックデジットを算出して設定する
//			buffer.append(getCheckDigit21(buffer.toString()));
//
//			String rltValue = buffer.toString();

			// 通番からサービス契約番号への変換を行う
			String rltValue = JKKSvcKeiNoSaiban.convSvcKeiNo(value);
			// 20160418 ANK-2783-00-00 お客さまＩＤ重複障害対応：採番ロジック見直し MOD END

			
			// サービス契約排他制御の検索条件の設定
			CAANMsg inETMsg = new CAANMsg(KK2111ETMsg.class.getName());
			inETMsg.set(KK2111ETMsg.SVC_KEI_NO, rltValue);
			KK2111LE le = new KK2111LE();
			// サービス契約排他制御検索
			CAANMsg outMsg = le.findByPrimaryKey(inETMsg);

			if (null == outMsg)
			{
				return rltValue;
			}
		}

		return null;
	}
}
