/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JKKMansGEnetUpdDlydCC
*	ソースファイル名：JKKMansGEnetUpdDlydCC.java
*	作成者			：FJ
*	日付			：2017年12月26日
*＜機能概要＞
*	マンションギガイーサ更新CC
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v35.00.00   2017/12/25   FJ)澤田     新規作成
*
**********************************************************************/
package com.fujitsu.futurity.bp.custom.common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.fujitsu.futurity.bp.x21.bpm.ServiceComponentRequestInvoker;
import com.fujitsu.futurity.bp.x21.bpm.db.SessionHandle;
import com.fujitsu.futurity.bp.x21.bpm.exception.RequestParameterException;
import com.fujitsu.futurity.bp.x21.bpm.parameter.IRequestParameterReadWrite;
import com.fujitsu.futurity.bp.x21.cc.AbstractCommonComponent;
import com.fujitsu.futurity.bp.x21.cc.exception.SCCallException;
import com.fujitsu.futurity.common.JCMConstants;
import com.fujitsu.futurity.common.x01.sc.SCControlMapKeys;
import com.fujitsu.futurity.mapping.bp.common.TemplateErrorUtil;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;

import eo.common.util.JKKStringUtil;
import eo.ejb.cbs.cbsmsg.ECC0111D010CBSMsg;
import eo.ejb.cbs.cbsmsg.EKK1051B002CBSMsg;

/**
 * マンションギガイーサ更新のディレイド処理を行います。<p>
 * <BR>
 * @author FJ)澤田
 */
public class JKKMansGEnetUpdDlydCC extends AbstractCommonComponent
{
	/** CBSMsgのテンプレートID項目 */
	private static final String TEMPLATEID = "templateID";

	/** CBSMsgの機能コード項目 */
	private static final String FUNC_CODE = "func_code";

	/** CBSMsgのステータス項目 */
	private static final String STATUS = "status";

	/** サービスID */
	private static final String SERVICE_ID = "KKSV088201CC";

	/** マンションギガイーサ更新ジョブID */
	private static final String JOB_ID = "EO2GN0119D0";
	
	/** 契約一時金一覧照会（サービス契約番号） */
	private static final String TEMPLATE_ID_EKK1051B002 = "EKK1051B002";
	
	/** ディレイド処理依頼登録 */
	private static final String TEMPLATE_ID_ECC0111D010 = "ECC0111D010";

	/** Unicode変換テーブル(EUC/Shift-JIS⇒MS932) */
	private static final char[][] UNI_CONV_1 = {
		{ 0x2014, 0x2015 },
		{ 0x2016, 0x2225 },
		{ 0x2212, 0xff0d },
		{ 0x301c, 0xff5e },
		{ 0x00a2, 0xffe0 },
		{ 0x00a3, 0xffe1 },
		{ 0x00ac, 0xffe2 },
	};

	/** Unicode変換テーブル(MS932⇒EUC/Shift-JIS)*/
	private static final char[][] UNI_CONV_2 = {
		{ 0x2015, 0x2014 },
		{ 0x2225, 0x2016 },
		{ 0xff0d, 0x2212 },
		{ 0xff5e, 0x301c },
		{ 0xffe0, 0x00a2 },
		{ 0xffe1, 0x00a3 },
		{ 0xffe2, 0x00ac },
	};

	/** 改行 CR + LF */
	private static final String CRLF = "\r\n";

	/**
	 * マンションギガイーサ更新処理
	 * 
	 * @param handle セッションハンドル
	 * @param param リクエストパラメータ
	 * @param fixedText サービスメッセージ
	 * @return リクエストパラメータ
	 * @throws Throwable スロー可能オブジェクト
	 */
	@SuppressWarnings("unchecked")
	public IRequestParameterReadWrite mansGEnetUpdDlyd(SessionHandle handle, IRequestParameterReadWrite param, String fixedText) throws Throwable
	{
		HashMap<String, Object> ccMsg = (HashMap<String, Object>)param.getData(fixedText);

		// 機能コード
		String funcCode = getMapValue(ccMsg, "func_code");
		
		// 運用日時
		String opeTime = JPCBPCommon.getOpeDateTimeStamp(null);
		// 運用日付
		String opeDate = JPCBPCommon.getOpeDate(null);
		// 契約一時金一覧照会（サービス契約番号）保持用リスト
		ArrayList EKK1051B002List = new ArrayList();
		
		// SC呼び出し部品
		ServiceComponentRequestInvoker scCall = new ServiceComponentRequestInvoker();
		
		// 更新対象リスト
		ArrayList<HashMap<String, Object>> trgtDataListUpd = (ArrayList<HashMap<String, Object>>)ccMsg.get("trgt_data_list_upd");
		for (int i = 0; i < trgtDataListUpd.size(); i++)
		{
			//契約一時金一覧照会（サービス契約番号）
			selectEKK1051(handle, param, scCall, opeDate, EKK1051B002List, trgtDataListUpd.get(i));
		}
		
		//契約一時金リストをセットする。
		ccMsg.put("EKK1051B002List", EKK1051B002List);
		
		//機能コードが2の場合、ディレイド処理は行わない。
		if ("2".equals(funcCode))
		{
			return param;
		}
		
		// ファイル編集
		byte[] txtData = getTxtData(ccMsg);
		// ディレイド処理依頼登録
		createEcc0111(handle, param, scCall, opeTime, txtData);
		
		return param;
	}

	/**
	 * エラー情報マッピング処理
	 * 
	 * <br>
	 * @param param リクエストパラメータ
	 * @param templates テンプレート
	 * @param returnCode リターンコード
	 * @return リクエストパラメータ
	 * @throws RequestParameterException リクエストパラメータ操作時の例外
	 */
	@SuppressWarnings("unchecked")
	public IRequestParameterReadWrite editErrorInfo(IRequestParameterReadWrite param, 
													CAANMsg[] templates,
													int returnCode) throws RequestParameterException
	{
		for (int i = 0; i < templates.length; i++)
		{
			CAANMsg template = templates[i];
			int templateStatus = template.getInt(STATUS);
			if (returnCode != 0)
			{
				templateStatus = 9000;
			}
			if (JCMAPLConstMgr.getString("RETURN_MESSAGE_" + String.format("%1$04d", templateStatus)) == null)
			{
				templateStatus = 0;
			}

			int bpStatus = 0;
			Object obj = param.getControlMapData(SCControlMapKeys.RETURN_CODE);
			if (obj == null)
			{
				bpStatus = -1;
			}
			else
			{
				bpStatus = Integer.parseInt((String)param.getControlMapData(SCControlMapKeys.RETURN_CODE));
			}

			if (templateStatus > bpStatus)
			{
				// BPにサービスコンポーネントのステータスを設定する。
				String formatStatus = String.format("%1$04d", templateStatus);
				String message = JCMAPLConstMgr.getString("RETURN_MESSAGE_" + formatStatus);
				param.setControlMapData(SCControlMapKeys.RETURN_CODE, formatStatus);
				param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, message);
			}

			HashMap inMap = (HashMap)param.getData(SERVICE_ID);

			HashMap mp = template.getHashMap();

			Iterator it = mp.keySet().iterator();
			while (it.hasNext())
			{
				String key = (String)it.next();
				if (key.endsWith("_err"))
				{
					int keyIdx = key.lastIndexOf("_err");
					if (inMap.containsKey(key.substring(0, keyIdx)))
					{
						inMap.put(key, mp.get(key));
					}
				}
			}
		}

		return param;
	}
	
	/**
	 * 契約一時金一覧照会（サービス契約番号）
	 * 
	 * @param handle セッションハンドル
	 * @param param リクエストパラメータ
	 * @param scCall SC呼出部品のインスタンス
	 * @param opeDate 運用日付
	 * @param EKK1051B002List 契約一時金一覧照会（サービス契約番号）保持用リスト
	 * @param map マップ
	 * @return メッセージ
	 * @throws Exception SC実行時の例外
	 */
	private void selectEKK1051(SessionHandle handle, 
			IRequestParameterReadWrite param, 
			ServiceComponentRequestInvoker scCall,
			String opeDate,
			ArrayList EKK1051B002List,
			HashMap<String, Object> map) throws Exception
	{
		String svcKeiNo = "";

		// ネット-サービス契約番号
		if (!JKKStringUtil.isNullBlank(getMapValue(map, "svc_kei_no")))
		{
			svcKeiNo = getMapValue(map, "svc_kei_no");
		}

		// 契約一時金一覧照会の検索パラメータを設定
		Object[][] eKK1051B002IN = {
				{EKK1051B002CBSMsg.KEY_SVC_KEI_NO, svcKeiNo},	// ＫＥＹ＿サービス契約番号
				{EKK1051B002CBSMsg.KEY_ICJKN_SETTE_APLY_MON, opeDate.substring(0, 6)}	// ＫＥＹ＿一時金設定適用月
		};

		// ◇ SC呼出実行
		CAANMsg eKK1051B002Msg = callSC(handle, param, scCall, TEMPLATE_ID_EKK1051B002, "1", eKK1051B002IN);

		// 処理結果
		CAANMsg[] eKK1051B002MsgList = eKK1051B002Msg.getCAANMsgList(EKK1051B002CBSMsg.EKK1051B002CBSMSG1LIST);
		if (eKK1051B002MsgList != null && 0 < eKK1051B002MsgList.length)
		{
			for (int i = 0; i < eKK1051B002MsgList.length; i++)
			{
				// 取消連携状態コード
				String clRenkeiStatCd = eKK1051B002MsgList[i].getString("cl_renkei_stat_cd");
				// 取消連携状態コードがNULLの場合
				if (JKKStringUtil.isNullBlank(clRenkeiStatCd))
				{
					HashMap<String, Object> paramMap = new HashMap<String, Object>();
					paramMap.put("svc_kei_no", svcKeiNo);
					// サービス契約番号を保存
					EKK1051B002List.add(paramMap);
					return;
				}
			}
		}
		return;
	}

	/**
	 * テキストデータを取得
	 * 
	 * @param ccMsg 更新対象リスト
	 * @return byte[]
	 * @throws Exception
	 */
	private byte[] getTxtData(
			HashMap<String, Object> ccMsg) throws Exception
	{
		StringBuilder sb = new StringBuilder();
		
		ArrayList<Object> trgtDataListUpd = new ArrayList<Object>();
		
		//1行目 START
		// 1列目　M-ID
		appendField(sb, (String)ccMsg.get("m_id"), true);	// M-IDを追加
		// 2列目　P-ID
		appendField(sb, (String)ccMsg.get("p_id"), false);	// P-IDを追加
		// 3列目　料金コース不一致のみフラグ
		appendField(sb, (String)ccMsg.get("pcrs_cd_huitti_flg"), false);	// 料金コース不一致のみフラグを追加
		// 4列目　ペアID
		appendField(sb, (String)ccMsg.get("pair_id"), false);	// ペアIDを追加
		// 5列目　子月額料金(1G)
		appendField(sb, (String)ccMsg.get("kanyu_kei_seiky_amnt"), false);	// 子月額料金(1G)を追加
		// 6列目　親月額料金
		appendField(sb, (String)ccMsg.get("ownr_kei_seiky_amnt"), false);	// 親月額料金を追加
		// 7列目　料金適用開始年月日
		appendField(sb, (String)ccMsg.get("pcrs_sta_ymd"), false);	// 料金適用開始年月日を追加
		// 8列目　回線使用契約番号
		appendField(sb, (String)ccMsg.get("kaisen_use_kei_no_net"), false);	// 回線使用契約番号を追加
		// 9列目　提供方式契約番号
		appendField(sb, (String)ccMsg.get("tk_hoshiki_kei_no"), false);	// 提供方式契約番号を追加
		// 10列目　子月額料金(100M)
		appendField(sb, (String)ccMsg.get("gtgk_prc_ko_100m"), false);	// 子月額料金(100M)を追加
		// 11列目　契約形態
		appendField(sb, (String)ccMsg.get("siharai_hoshiki_cd"), false);	// 契約形態を追加
		// 12列目　総戸数
		appendField(sb, (String)ccMsg.get("mans_bkn_inf_kosu"), false);	// 総戸数を追加
		//1行目 END
		trgtDataListUpd = (ArrayList<Object>)ccMsg.get("trgt_data_list_upd");
		sb.append(CRLF);
		//2行目 以降 START
		// ***** 項目値取得 *****
		for (int i = 0; i < trgtDataListUpd.size(); i++)
		{
			HashMap<String, Object> childMap = new HashMap<String, Object>();
			childMap = (HashMap<String, Object>)trgtDataListUpd.get(i);
			// 1列目　サービス契約番号
			appendField(sb, (String)childMap.get("svc_kei_no"), true);	// サービス契約番号を追加
			// 2列目　最終更新年月日時分秒
			appendField(sb, (String)childMap.get("last_upd_dtm"), false);	// 最終更新年月日時分秒を追加

			sb.append(CRLF);
		}

		// 文字コード変換
		String txtData = charConverter(sb.toString(), "Shift-JIS");

		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "txtData>>>" + txtData);

		//SJISのバイト配列に変換して返す
		return txtData.getBytes("Shift_JIS");
	}

	/**
	 * 列の値を追加
	 * 
	 * @param sb 追加先のStringBuilder
	 * @param value 追加する値
	 * @param firstField 先頭項目かどうか
	 */
	private void appendField(StringBuilder sb, String value, boolean firstField)
	{
		if (!firstField)
		{
			sb.append(",");
		}
		if (!JKKStringUtil.isNullBlank(value))
		{
			sb.append("\"" + value + "\"");
		}
		else 
		{
			sb.append("\"" + value + "\"");
		}
	}

	/**
	 * ディレイド処理依頼登録処理
	 * 
	 * @param handle セッションハンドル
	 * @param param リクエストパラメータ
	 * @param scCall SC呼出部品のインスタンス
	 * @param opeTime 運用日時
	 * @param efile 電子ファイル
	 * @return メッセージ
	 * @throws Exception SC実行時の例外
	 */
	private CAANMsg createEcc0111(SessionHandle handle, 
			IRequestParameterReadWrite param, 
			ServiceComponentRequestInvoker scCall,
			String opeTime,
			byte[] efile) throws Exception
	{
		// 運用日取得
		// ディレイド処理依頼登録の入力パラメータを設定
		Object[][] ecc0111d010IN = {
				{ECC0111D010CBSMsg.DLYD_TRN_JSSI_UNYO_DTM,	opeTime},	// ディレイド処理実施運用年月日時分秒
				{ECC0111D010CBSMsg.REQ_JOB_ID,	JOB_ID},	// 依頼ジョブID
				{ECC0111D010CBSMsg.DLYD_TRN_REQ_SBT_CD,	""},	// ディレイド処理依頼種別コード
				{ECC0111D010CBSMsg.FILE_CD,	""},	// ファイルコード
				{ECC0111D010CBSMsg.EFILE,	efile},	// 電子ファイル
				{ECC0111D010CBSMsg.FILE_DEL_YMD,	""}	// ファイル削除年月日
		};

		// ◇ SC呼出実行
		return callSC(handle, param, scCall, TEMPLATE_ID_ECC0111D010, "1", ecc0111d010IN);
	}

	/**
	 * SC呼出処理
	 * 
	 * @param handle セッションハンドル
	 * @param param リクエストパラメータ
	 * @param scCall SC呼出部品のインスタンス
	 * @param svcIfId サービスIFのID
	 * @param funcCode 機能コード
	 * @param svcIfMapData 上り設定用データ
	 * @return メッセージ
	 * @throws Exception SC実行時の例外
	 */
	@SuppressWarnings("unchecked")
	private CAANMsg callSC(SessionHandle handle, 
							IRequestParameterReadWrite param, 
							ServiceComponentRequestInvoker scCall,
							String svcIfId,
							String funcCode,
							Object[][] svcIfMapData) throws Exception
	{
		// ◇ 上りマッピング処理
		// 上りマッピングの処理結果を取得するためのマップを生成
		HashMap<String, Object> paramMap = editInMsg(param, svcIfId, funcCode, svcIfMapData);

		// SC呼出実行
		Map<?, ?> result = scCall.run(paramMap, handle);

		// 処理結果の判定
		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);

		// 取得したリターンコード、ステータスの内容を見て異常かどうかの判断をする。
		String rtnCode = result.get(JCMConstants.RET_CD_INT_KEY).toString();

		// エラー情報の転記
		editErrorInfo(param, templates, (Integer)result.get(JCMConstants.RET_CD_INT_KEY));

		// エラー情報のマップを取得
		ArrayList<Object> errList = (ArrayList<Object>)param.getControlMapData(SCControlMapKeys.ERROR_INFO);
		if (errList == null)
		{
			errList = new ArrayList<Object>();
		}
		// コントロールマップに設定
		param.setControlMapData(SCControlMapKeys.ERROR_INFO, TemplateErrorUtil.getErrorInfo(result, errList));

		int status = templates[0].getInt(STATUS);
		// 異常の場合、SCCallExceptionを生成してスローする
		if (!("0".equals(rtnCode) && 0 == status))
		{
			throw new SCCallException("戻り値不正", rtnCode, status);
		}

		return ((CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY))[0];
	}

	/**
	 * 上りマッピング処理
	 * 
	 * @param param リクエストパラメータ
	 * @param svcIfId サービスIFのID
	 * @param funcCode 機能コード
	 * @param mapData 上り設定用データ
	 * @return 上りを設定したマップ
	 * @throws RequestParameterException リクエストパラメータ操作時の例外
	 */
	private HashMap<String, Object> editInMsg(IRequestParameterReadWrite param, 
												String svcIfId, 
												String funcCode,
												Object[][] mapData) throws RequestParameterException
	{
		HashMap<String, Object> paramMap = new HashMap<String, Object>();

		// 【取得元：電文ヘッダ(ヘッダ)】
		// 電文ID
		paramMap.put(JCMConstants.TRANZACTION_ID_KEY, param.getTelegramID());
		// ユースケースID
		paramMap.put(JCMConstants.USECASE_ID_KEY, param.getUsecaseID());
		// オペレーションID
		paramMap.put(JCMConstants.OPERATION_ID_KEY, param.getOperationID());
		// サービス呼び出し区分
		paramMap.put(JCMConstants.CALL_TYPE_KEY, param.getCallType());

		// 【取得元：ユーザエリア(コントロールマップ)】
		// 依頼先ホスト名
		paramMap.put(JCMConstants.CLIENT_HOST_NAME_KEY, param.getControlMapData(SCControlMapKeys.REQ_HOSTNAME));
		// 依頼元IPアドレス
		paramMap.put(JCMConstants.CLIENT_IP_ADDRESS_KEY, param.getControlMapData(SCControlMapKeys.REQ_HOSTIP));
		// 依頼元画面ID
		paramMap.put(JCMConstants.INVOKE_GAMEN_ID_KEY, param.getControlMapData(SCControlMapKeys.REQ_VIEWID));
		// オペレータID
		paramMap.put(JCMConstants.OPERATOR_ID_KEY, param.getControlMapData(SCControlMapKeys.OPERATOR_ID));

		CAANMsg template = new CAANMsg("eo.ejb.cbs.cbsmsg." + svcIfId + "CBSMsg");

		// テンプレートID
		template.set(TEMPLATEID, svcIfId);

		template.set(FUNC_CODE, funcCode);

		// オペレータID
		Object operatorId = param.getControlMapData(SCControlMapKeys.OPERATOR_ID);
		template.set(JCMConstants.OPERATOR_ID_KEY, operatorId);

		// 運用日付
		Object operateDate = param.getControlMapData(SCControlMapKeys.OPE_DATE);
		template.set(JCMConstants.OPERATE_DATE_KEY, operateDate);

		// 運用日時
		Object operateDateTime = param.getControlMapData(SCControlMapKeys.OPE_TIME);
		template.set(JCMConstants.OPERATE_DATETIME_KEY, operateDateTime);

		for (int i = 0; i < mapData.length; i++)
		{
			Object[] mapVal = mapData[i];
			if (mapVal[1] instanceof byte[])
			{
				template.set((String)mapVal[0], (byte[])mapVal[1]);
			}
			else
			{
				if ("".equals(mapVal[1]))
				{
					template.setNull((String)mapVal[0]);
				}
				else
				{
					template.set((String)mapVal[0], (String)mapVal[1]);
				}
			}
		}

		CAANMsg[] templates = new CAANMsg[1];
		templates[0] = template;
		paramMap.put(JCMConstants.TEMPLATE_LIST_KEY, templates);

		return paramMap;
	}
	
	/**
	 * マップから値を取得
	 * 
	 * @param map 値を取り出すマップ
	 * @param key キー
	 * @return 取り出した値
	 */
	private String getMapValue(HashMap<String, Object> map, String key)
	{
		String str = (String)map.get(key);

		if (str == null)
		{
			return "";
		}

		return str;
	}

	/**
	 * 文字コードの変換を行います。<br>
	 * Unicodeコードポイントの強制変換します。
	 * <br>
	 * @param value コード変換対象の文字列
	 * @param charCode 出力する文字コードを「UTF-8」「Shift-JIS」「EUC-JP」「DEFAULT」で指定する。<br>
	 * 　　　　　　　　 ※「DEFAULT」はOSに準拠した文字コード
	 * @return 変換後の文字列
	 */
	private String charConverter(String value, String charCode)
	{
		// Shift-JIS・EUC-JPのみ変換用の変換
		if ("Shift-JIS".equals(charCode) || "EUC-JP".equals(charCode))
		{
			return changeUnicode(value, UNI_CONV_2);
		}
		// その他の変換（UTF-8）
		else
		{
			return changeUnicode(value, UNI_CONV_1);
		}
	}

	/**
	 * Unicodeの強制変換を行います。
	 * <br>
	 * @param arg0 変換対象の文字列
	 * @param convList Unicode変換定義
	 * @return 変換後文字列
	 */
	private String changeUnicode(String arg0, char[][] convList)
	{
		if (JKKStringUtil.isNullBlank(arg0))
		{
			return arg0;
		}

		char[] chars = arg0.toCharArray();

		for (int i = 0; i < chars.length; i++)
		{
			chars[i] = transUnicode(chars[i], convList);
		}

		return new String(chars);
	}

	/**
	 * 指定された変換定義に従ってUnicodeの強制変換を行います。
	 * <br>
	 * @param c Unicode変換する文字
	 * @param convList Unicode変換定義
	 * @return Unicode変換された文字
	 */
	private char transUnicode(char c, char[][] convList)
	{
		for (int i = 0; i < convList.length; i++)
		{
			char[] chars = convList[i];

			if (chars[0] == c)
			{
				return chars[1];
			}
		}

		return c;
	}
}
