package com.fujitsu.futurity.bp.custom.common;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.fujitsu.futurity.bp.x21.bpm.exception.RequestParameterException;
import com.fujitsu.futurity.bp.x21.bpm.parameter.IRequestParameterReadOnly;
import com.fujitsu.futurity.bp.x21.bpm.parameter.IRequestParameterReadWrite;
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.base.CAANSchemaInfo;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;

/**
 * 電話ＳＯＤ依頼受付法人サービスコンポーネント呼出用のパラメータ編集を行います。
 * <br />
 * @author 富士通
 */
public class JSCTelSvcScParamHenshu
{

	/**
	 * SC呼出用のパラメータを作成します。
	 * @param param リクエストパラメータ
	 * @param templateId テンプレートID
	 * @param cbsMsgClass CBSMsgクラス
	 * @param param4serviceIf サービスインターフェイス用パラメータ
	 * @param funcCode 機能コード
	 * @return SC呼出用パラメータ
	 * @throws RequestParameterException
	 */
	@SuppressWarnings("unchecked")
	public static HashMap<String, Object> createScParam(IRequestParameterReadOnly param, String templateId, Class<?> cbsMsgClass,
															Map param4serviceIf, String funcCode) throws RequestParameterException
	{

		// 受け取ったリクエストパラメータを元に、ＳＣ呼出用の上りマップを生成する。
		HashMap<String, Object> paramMap = new HashMap<String, Object>();

		CAANMsg[] templates = new CAANMsg[1];

		CAANMsg template = new CAANMsg(cbsMsgClass.getName());

		// **********************************************
		// 【取得元：電文ヘッダ(ヘッダ)】
		// **********************************************
		// 電文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());

		// **********************************************
		// 【取得元：ユーザエリア(コントロールマップ)】
		// **********************************************
		// 依頼先ホスト名
		Object clientHostName = param.getControlMapData(SCControlMapKeys.REQ_HOSTNAME);
		paramMap.put(JCMConstants.CLIENT_HOST_NAME_KEY, clientHostName);

		// 依頼元IPアドレス
		Object clientIPAddress = param.getControlMapData(SCControlMapKeys.REQ_HOSTIP);
		paramMap.put(JCMConstants.CLIENT_IP_ADDRESS_KEY, clientIPAddress);

		// 依頼元画面ID
		Object clientViewId = param.getControlMapData(SCControlMapKeys.REQ_VIEWID);
		paramMap.put(JCMConstants.INVOKE_GAMEN_ID_KEY, clientViewId);

		// オペレータID
		Object operatorId = param.getControlMapData(SCControlMapKeys.OPERATOR_ID);
		paramMap.put(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);

		// **********************************************
		// 【取得元：ユーザデータ
		// **********************************************

		// テンプレートIDのセット
		paramMap.put(JCMConstants.TEMPLATE_ID_KEY, templateId);
		template.set(JCMConstants.TEMPLATE_ID_KEY, templateId);
		template.set(JCMConstants.FUNC_CODE_KEY, funcCode);

		// 値の指定がない項目にnullをセットする。
		setNullOtherColumn(cbsMsgClass, param4serviceIf);

		// 値がブランクのものをnullに置き換える
		setNullBlankColumn(param4serviceIf);

		// 入力パラメータのセット
		template.setMsgDataOfAll((HashMap)param4serviceIf);
		templates[0] = template;

		paramMap.put("templates", templates);

		return paramMap;
	}

	/**
	 * 入力パラメータ値の指定がない項目にnullをセットする。<br/>
	 * @param cbsMsgClass CBSMsgクラス
	 * @param param4serviceIf サービスインターフェイス用パラメータ
	 */
	@SuppressWarnings("unchecked")
	public static void setNullOtherColumn(Class<?> cbsMsgClass, Map param4serviceIf)
	{

		try
		{
			java.lang.reflect.Field fieldContents = cbsMsgClass.getDeclaredField("CONTENTS");
			fieldContents.setAccessible(true);
			Object[][] contents = (Object[][])fieldContents.get(cbsMsgClass);

			// サービスインターフェイスの列情報を取得
			Map<String, String> colInfo = new HashMap<String, String>();
			for(int i = 0; i < contents.length; i++)
			{
				Object[] obj = contents[i];
				colInfo.put(obj[0].toString(), obj[1].toString());
			}

			/* エラーフィールド(項目末尾が"_err"の項目)の定義されている項目を入力項目と見なし
			   当該フィールドについて、パラメータ値の指定がない場合は項nullをセットする */
			for(int i = 0; i < contents.length; i++)
			{
				Object[] obj = contents[i];
				String errFieldName = obj[0].toString();
				if(errFieldName.matches(".*_err$"))
				{
					String fieldName = errFieldName.replaceAll("_err$", "");
					String colDataType = colInfo.get(fieldName);
					if(("String".equals(colDataType)) || ("Long".equals(colDataType)))
					{
						// 指定がない項目にnullをセット
						if(!param4serviceIf.containsKey(fieldName))
						{
							param4serviceIf.put(fieldName, null);
						}
					}
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * 値がブランクのものをnullに置き換える。<br/>
	 * @param param4serviceIf サービスインターフェイス用入力パラメータ
	 */
	@SuppressWarnings("unchecked")
	public static void setNullBlankColumn(Map param4serviceIf)
	{
		// TODO リスト対応
		Iterator it = param4serviceIf.keySet().iterator();
		while(it.hasNext()) 
		{
			String key = (String)it.next();
			Object valObj = param4serviceIf.get(key);
			if(valObj instanceof String)
			{
				if("".equals((String)valObj))
				{
					param4serviceIf.put(key, null);
				}
			}
		}
	}


	/**
	 * メッセージキャリアクラスの各項目のデータをマップに変換する。
	 * (全項目が対象となる為、変換不要な項目は変換後のマップから除外する事。)
	 * <br />
	 * @param msgObj CAANMsg
	 * @param msgClassFrom コピー元のメッセージキャリアクラス
	 * @param msgClassTo コピー先のメッセージキャリアクラス
	 * @return Mapに変換されたCAANMsg
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> copyMsgToMap(CAANMsg msgObj, Class msgClassFrom, Class msgClassTo) 
			throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, 
					IllegalArgumentException, InvocationTargetException
	{

		Map<String, Object> map = new HashMap<String, Object>();

		Map<String, Object> colInfo1 = new HashMap<String, Object>();
		CAANSchemaInfo tmpSchemaInfo1 = (CAANSchemaInfo)msgClassFrom.newInstance();
		Method getContentsMethod1 = msgClassFrom.getMethod("getContents");
		getContentsMethod1.setAccessible(true);
		Object[][] contents1 = (Object[][])getContentsMethod1.invoke(tmpSchemaInfo1);
		for (Object[] recObj : contents1) {
			String columnName = (String)recObj[0];
			//String columnType = (String)recObj[1];
			String colValue   = msgObj.getString(columnName);
			colInfo1.put(columnName, colValue);
		}

		CAANSchemaInfo tmpSchemaInfo2 = (CAANSchemaInfo)msgClassTo.newInstance();
		Method getContentsMethod2 = msgClassTo.getMethod("getContents");
		getContentsMethod2.setAccessible(true);
		Object[][] contents2 = (Object[][])getContentsMethod2.invoke(tmpSchemaInfo2);
		for(Object[] recObj : contents2)
		{
			String columnName = (String)recObj[0];
			if(colInfo1.containsKey(columnName))
			{
				Object colValue = colInfo1.get(columnName);
				map.put(columnName, colValue);
			}
		}

		return map;
	}

	/**
	 * 下り電文コントロールマップ、及び ユーザデータを設定します。
	 * @param result サービスコンポーネントの実行結果
	 * @param param リクエストパラメータ
	 * @param scMapkey サービスコンポーネントID
	 * @param resUserData 追加でユーザデータに設定する情報
	 * @throws Throwable
	 */
	@SuppressWarnings("unchecked")
	public static void setResultAll(Map<?, ?> result,
			IRequestParameterReadWrite param, String scMapkey, Map resUserData) throws Throwable
	{

		// 下りユーザデータマップのセット
		setResultUserData(param, scMapkey, resUserData);

		// 下り電文コントロールマップのセット
		setResultCtrlData(param, result, scMapkey);
	}

	/**
	 * 下り電文コントロールマップのセット。
	 * @param param リクエストパラメータ
	 * @param result SCの実行結果
	 * @param scMapkey SCのID
	 * @param returnCd リターンコード
	 * @param status ステータス
	 * @throws Throwable
	 */
	@SuppressWarnings("unchecked")
	public static void setResultCtrlData(IRequestParameterReadWrite param, Map<?, ?> result, String scMapkey, 
											Object returnCd, Object status) throws Throwable
	{

		int returnCode = new Integer(returnCd.toString());
		int templateStatus = new Integer(status.toString());

		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);
		}

		//エラー情報のマップを取得
		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));

	}

	/**
	 * 下り電文コントロールマップのセット。
	 * @param param リクエストパラメータ
	 * @param result SCの実行結果
	 * @param scMapkey SCのID
	 * @throws Throwable
	 */
	public static void setResultCtrlData(IRequestParameterReadWrite param, Map<?, ?> result, String scMapkey) throws Throwable 
	{

		Object returnCd = result.get(JCMConstants.RET_CD_INT_KEY);
		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);
		CAANMsg caanMsg = templates[0];
		Object status   = caanMsg.getInt(JCMConstants.STATUS_INT_KEY);

		setResultCtrlData(param, result, scMapkey, returnCd, status);
	}

	/**
	 * 下りユーザデータマップのセット。
	 * @param param リクエストパラメータ
	 * @param scMapkey SCのID
	 * @param userData 追加でセットするユーザデータ
	 * @throws Throwable
	 */
	@SuppressWarnings("unchecked")
	public static void setResultUserData(IRequestParameterReadWrite param, String scMapkey, Map userData) throws Throwable
	{

		// SCから取得したデータのセット
		if(userData != null)
		{
			Map<String, Object> userDataRef = (Map<String, Object>)param.getData(scMapkey);
			Iterator iterator = userData.keySet().iterator();
			while(iterator.hasNext())
			{
				String mapKey = (String)iterator.next();
				Object mapVal = userData.get(mapKey);
				userDataRef.put(mapKey, mapVal);
			}
		}
	}

	/**
	 * ユーザデータのセット。
	 * @param param リクエストパラメータ
	 * @param scMapkey SCのID
	 * @param itemKey  追加項目のID
	 * @param itemVal  追加項目
	 * @throws Throwable
	 */
	@SuppressWarnings("unchecked")
	public static void setUserData(IRequestParameterReadWrite param, String scMapkey, String itemKey, String itemVal) throws Throwable
	{

		// SCから取得したデータのセット
		Map<String, Object> userDataRef = (Map<String, Object>)param.getData(scMapkey);
		userDataRef.put(itemKey, itemVal);
	}

	/**
	 * SCの返却情報からCAANMsgリストを取得する。
	 * @param result SCの返却情報
	 * @param listName リスト名
	 * @return CAANMsgリスト
	 */
	public static CAANMsg[] getResultMsgList(Map<?, ?> result, String listName)
	{
		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);
		CAANMsg parentTemplate = templates[0];
		CAANMsg[] templateArray = parentTemplate.getCAANMsgList(listName);
		if (templateArray == null)
		{
			templateArray = new CAANMsg[]{};
		}
		return templateArray;
	}

	/**
	 * SCの返却情報から任意の項目のデータを文字列で取得する。
	 * @param result SCの返却情報
	 * @param columnName 項目名
	 * @return 対象項目のデータ
	 */
	public static String getResultString(Map<?, ?> result, String columnName)
	{
		String strVal = null;

		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);
		CAANMsg caanMsg = templates[0];
		String[] colNames = columnName.split("\\.");
		for(int i = 0; i < colNames.length; i++)
		{
			String tmpColName = colNames[i];
			if(tmpColName.matches(".+\\[[0-9]+\\]$"))
			{
				String listName = tmpColName.replaceAll("\\[[0-9]+\\]", "");
				String indexStr = tmpColName.replace(listName, "").replaceAll("(\\[|\\])", "");;
				int index = Integer.parseInt(indexStr);
				if(caanMsg != null)
				{
					CAANMsg[] listData = caanMsg.getCAANMsgList(listName);
					if(listData == null)
					{
						break;
					}
					else if ((listData.length - 1) >= index)
					{
						caanMsg = listData[index];
					}
					else
					{
						break;
					}
				}
			}
			else
			{
				if(i == (colNames.length - 1))
				{
					if(caanMsg != null)
					{
						strVal = caanMsg.getString(tmpColName);
					}
				}
				else
				{
					if(caanMsg != null)
					{
						caanMsg = caanMsg.getCAANMsg(tmpColName);
					}
				}
			}
		}
		return strVal;
	}

	/**
	 * SCの返却情報から任意の項目のデータを数値で取得する。
	 * @param result SCの返却情報
	 * @param columnName 項目名
	 * @return 対象項目のデータ
	 */
	public static Integer getResultInteger(Map<?, ?> result, String columnName)
	{
		
		Integer intVal = null;

		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);
		CAANMsg caanMsg = templates[0];
		String[] colNames = columnName.split("\\.");
		for(int i = 0; i < colNames.length; i++)
		{
			String tmpColName = colNames[i];
			if (tmpColName.matches(".+\\[[0-9]+\\]$"))
			{
				String listName = tmpColName.replaceAll("\\[[0-9]+\\]", "");
				String indexStr = tmpColName.replace(listName, "").replaceAll("(\\[|\\])", "");;
				int index = Integer.parseInt(indexStr);
				CAANMsg[] listData = caanMsg.getCAANMsgList(listName);
				caanMsg = listData[index];
			}
			else
			{
				if(i == (colNames.length - 1))
				{
					intVal = caanMsg.getInt(tmpColName);
				}
				else
				{
					caanMsg = caanMsg.getCAANMsg(tmpColName);
				}
			}
		}
		return intVal;
	}

	/**
	 * サービスインターフェイスのエラー有無を判定する
	 * @param result サービスインターフェイス実行結果
	 * @return エラー有無(true:エラー有り、false:エラー無し)
	 */
	public static boolean isError(Map<?, ?> result)
	{
		return !isSuccess(result);
	}

	/**
	 * サービスインターフェイスのエラー有無を判定する
	 * @param result サービスインターフェイス実行結果
	 * @return エラー有無(true:エラー無し、false:エラー有り)
	 */
	public static boolean isSuccess(Map<?, ?> result)
	{
		Integer returnCd = getReturnCode(result);
		Integer status   = getStatus(result);
		
		return (returnCd.compareTo(0) == 0) && (status.compareTo(0) == 0);
	}

	/**
	 * SCの実行結果からリターンコードを取得します。
	 * @param result SCの実行結果
	 * @return リターンコード
	 */
	public static Integer getReturnCode(Map<?, ?> result)
	{
		return (Integer)result.get(JCMConstants.RET_CD_INT_KEY);
	}
	
	/**
	 * SCの実行結果からステータスを取得します。
	 * @param result SCの実行結果
	 * @return ステータス
	 */
	public static Integer getStatus(Map<?, ?> result)
	{

		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);
		CAANMsg msg = templates[0];
		return msg.getInt(JCMConstants.STATUS_INT_KEY);
	}

}
