package com.eactive.eai.rms.onl.transaction.tracking;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.apim.portal.monitoringcode.entity.MonitoringCodeId;
import com.eactive.eai.common.EAITable;
import com.eactive.eai.common.header.HeaderAction;
import com.eactive.eai.common.header.HeaderActionFactory;
import com.eactive.eai.common.header.HeaderActionKeys;
import com.eactive.eai.common.logger.HttpAdapterExtraLogVo;
import com.eactive.eai.common.logger.mapper.HttpAdapterExtraLogMapper;
import com.eactive.eai.common.util.MessageUtil;
import com.eactive.eai.data.entity.onl.logger.EAILogId;
import com.eactive.eai.data.entity.onl.logger.HttpAdapterExtraLog;
import com.eactive.eai.data.entity.onl.logger.HttpAdapterExtraLogId;
import com.eactive.eai.data.entity.onl.logger.SubMessageLog;
import com.eactive.eai.data.entity.onl.logger.SubMessageLogId;
import com.eactive.eai.data.entity.onl.message.ServiceMessageEntity;
import com.eactive.eai.data.entity.onl.message.ServiceMessageEntityId;
import com.eactive.eai.data.entity.onl.transformer.layout.LayoutEntity;
import com.eactive.eai.data.entity.onl.transformer.transform.TransformEntity;
import com.eactive.eai.data.entity.onl.transformer.transform.TransformTypeEntity;
import com.eactive.eai.message.StandardLogItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.StandardMessageUtil;
import com.eactive.eai.message.parser.FlatReader;
import com.eactive.eai.message.parser.StandardReader;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.common.combo.ComboService;
import com.eactive.eai.rms.common.combo.ComboVo;
import com.eactive.eai.rms.common.login.SessionManager;
import com.eactive.eai.rms.data.entity.man.monitoringCode.service.MonitoringCodeService;
import com.eactive.eai.rms.data.entity.onl.eaimsg.ServiceMessageService;
import com.eactive.eai.rms.data.entity.onl.layout.LayoutService;
import com.eactive.eai.rms.data.entity.onl.logger.EAILogDetailSearch;
import com.eactive.eai.rms.data.entity.onl.logger.EAILogDto;
import com.eactive.eai.rms.data.entity.onl.logger.EAILogSearch;
import com.eactive.eai.rms.data.entity.onl.logger.EAILogService;
import com.eactive.eai.rms.data.entity.onl.logger.HttpAdapterExtraLogService;
import com.eactive.eai.rms.data.entity.onl.logger.SubMessageLogService;
import com.eactive.eai.rms.data.entity.onl.transform.TransformService;
import com.eactive.eai.rms.onl.common.exception.EMSRuntimeException;
import com.eactive.eai.rms.onl.manage.adapter.adapter.AdapterManService;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUI;
import com.eactive.eai.rms.onl.manage.rule.transform.TransformVo;
import com.eactive.eai.rms.onl.manage.serviceinfo.bizkey.FieldExtractInfoService;
import com.eactive.eai.transformer.SystemKeys;
import com.eactive.eai.transformer.dao.jdbc.LayoutJdbcDao;
import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.Layout;
import com.eactive.eai.transformer.layout.LayoutTypeManager;
import com.eactive.eai.transformer.message.Message;
import com.eactive.eai.transformer.message.MessageSetDataException;
import com.eactive.eai.transformer.message.MonitoringVBytesMessage;
import com.eactive.eai.transformer.message.MonitoringVEbcdicMessage;
import com.eactive.eai.transformer.message.XMLMessage;
import com.eactive.eai.transformer.util.IConv;
import com.eactive.eai.util.HexaConverter;
import com.ext.eai.common.stdmessage.STDMessageKeys;

@Service("dbTrackingService")
@Transactional
public class DbTrackingService extends BaseService {

	public static final int LIMIT_COUNT = 10000;
	public static final String LIMIT_ERROR_CODE = "LIMIT_ERROR";
	public static final String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
	public static final String LIMIT_ERROR_MSG = "조회결과가 " + LIMIT_COUNT
							+ "건이 넘습니다.\n\n조회 조건을 변경하여 주십시오.";

	public static final int GUID_LENGTH = 30; // seq값 제외부분
	public static final int GUID_DATE_POSITION = 3;
	public static final int DATE_FORMAT_OFFSET = 0;

	private final ComboService comboService;
	private final EAILogService eaiLogService;
	private final EAILogUIMapper eaiLogUIMapper;
	private final SubMessageLogService subMessageLogService;
	private final MonitoringCodeService monitoringCodeService;
	private final HttpAdapterExtraLogService httpAdapterExtraLogService;
	private final HttpAdapterExtraLogMapper httpAdapterExtraLogMapper;
	private final FieldExtractInfoService fieldExtractInfoService;
	private final ServiceMessageService serviceMessageService;
	private final TransformService transformService;
	private final LayoutJdbcDao layoutJdbcDao;
	private final LayoutService layoutService;
	private final PersonalDataEncryptConverter encryptConverter = new PersonalDataEncryptConverter();

	@Autowired
	private AdapterManService adapterManService;


	@Autowired
	public DbTrackingService(ComboService comboService,
							EAILogService eaiLogService,
							EAILogUIMapper eaiLogUIMapper,
							SubMessageLogService subMessageLogService,
							MonitoringCodeService monitoringCodeService,
							HttpAdapterExtraLogService httpAdapterExtraLogService,
							HttpAdapterExtraLogMapper httpAdapterExtraLogMapper,
							FieldExtractInfoService fieldExtractInfoService,
							ServiceMessageService serviceMessageService,
							TransformService transformService,
							LayoutJdbcDao layoutJdbcDao,
							LayoutService layoutService) {
		this.comboService = comboService;
		this.eaiLogService = eaiLogService;
		this.eaiLogUIMapper = eaiLogUIMapper;
		this.subMessageLogService = subMessageLogService;
		this.monitoringCodeService = monitoringCodeService;
		this.httpAdapterExtraLogService = httpAdapterExtraLogService;
		this.httpAdapterExtraLogMapper = httpAdapterExtraLogMapper;
		this.fieldExtractInfoService = fieldExtractInfoService;
		this.serviceMessageService = serviceMessageService;
		this.transformService = transformService;
		this.layoutJdbcDao = layoutJdbcDao;
		this.layoutService = layoutService;
	}

	public Page<EAILogUI> selectList(Pageable pageable,
							EAILogSearch eaiLogSearch) {

		// 개발일 경우엔 권한상관없이 모든업무 보여지게
//		if (SystemUtil.isProdServer()) {
//			eaiLogSearch.setAuthChk("Y");
//		}
		//추후 권한 체크를 할 경우를 대비하여 로그인 아이디 추가
		String loginId	= SessionManager.getLoginVo().getUserId();
		eaiLogSearch.setLoginId(loginId);

		// GUID가 검색조건에 있는 경우 GUID 인덱스를 타기 위해서 다른검색조건은 제외한다.
		// GUID 가 UUIDGenerator.GUID_LENGTH 자리 이상이어야만 거래 조회 가능
		if (StringUtils.isNotBlank(eaiLogSearch.getSearchGuid())
								&& eaiLogSearch.getSearchGuid()
														.length() < GUID_LENGTH) {
			throw new EMSRuntimeException("GUID must be at least " + GUID_LENGTH
									+ " digits.");
		}

		Page<EAILogUI> page = eaiLogService.findAll(pageable, eaiLogSearch)
								.map(eaiLogUIMapper::toVo);

		if (page.getSize() == 0) {
			throw new EMSRuntimeException("조회결과가 없습니다.");
		}

		// 모니터링 기관코드 받아오기
		page.forEach(eaiLog -> {
			String adapterInstiCode = eaiLog.getAdptrinsticode();

			if (StringUtils.isNotEmpty(adapterInstiCode)) {
				String codeName = getAdptrinstiname(adapterInstiCode);
				eaiLog.setAdptrinstiname(codeName);
			}

		});

		return page;
	}

	public Map<String, Long> getTransactionCounts(EAILogSearch eaiLogSearch) {
		return eaiLogService.getTransactionCounts(eaiLogSearch);
	}


	private String getAdptrinstiname(String adapterInstiCode) {
		return monitoringCodeService.findCodenameById(new MonitoringCodeId(
				"ADPTR_DESC", adapterInstiCode));
	}

	public EAILogUI selectDetail(EAILogDetailSearch eaiLogDetailSearch)
							throws Exception {

		EAILogId eaiLogId = new EAILogId();
		eaiLogId.setEaibzwkdstcd(eaiLogDetailSearch.getEaiBzwkDstcd());
		eaiLogId.setMsgdpstyms(eaiLogDetailSearch.getMsgDpstYMS());
		eaiLogId.setEaisvcserno(eaiLogDetailSearch.getEaiSvcSerno());
		eaiLogId.setLogprcssserno(eaiLogDetailSearch.getLogPrcssSerno());

		EAILogDto eaiLogDto = eaiLogService.getById(eaiLogId);
		EAILogUI eaiLogUI = eaiLogUIMapper.toVo(eaiLogDto);
		
		logger.info("eaiLogUI : " + eaiLogUI.toString() );

		try {
			// 모니터링 기관코드 받아오기
			String adapterInstiCode = eaiLogUI.getAdptrinsticode();

			if (StringUtils.isNotBlank(adapterInstiCode)) {
				String codeName = getAdptrinstiname(adapterInstiCode);
				eaiLogUI.setAdptrinstiname(codeName);
			}

		} catch (Exception ex) {
			logger.error("", ex);
		}

		String bizData = eaiLogUI.getBzwkdatactnt();
		/**
		 * 업무데이터에 대한정의 1. biz 데이터 2. 표준전문 JSON 데이터 3. 전장표
		 */
		String header = "";
		String data = "";
		// mdclData 선언은 되어 있으나 사용하지 않음. 20204.04.04
		// String mdclData = "";
		String[] split = bizData.split("[|]");
				
		// 최종적으로는 3개여야됨
		if (split.length > 0) {
			header = split[0];
		}
		
		if (split.length > 1) {
			data = split[1];
		}
		
		data = extractDataBetweenPipes(bizData);
		
		if(data == null) {	
			data = extractDataBetweenPipes2(bizData);
		}

		
		if (!StringUtils.isBlank(data)) {
			eaiLogUI.setBzwkdatactnt(encryptConverter.convertToEntityAttribute(data));
		} else {
			eaiLogUI.setBzwkdatactnt("");
		}

		// HEADER
		StandardMessage message = null;
		if (!StringUtils.isBlank(header)) {
			try {
				header = new String(HexaConverter.hexaToBytes(header),
										"euc-kr");
			} catch (Exception e) {}

			String stdLayout = eaiLogUI.getStdlayoutname();

			try {
				message = StandardMessageUtil.generateMessageFromDb(stdLayout);
								
				StandardReader reader = new FlatReader();
				reader.parse(message, header);
				ArrayList<StandardLogItem> logItems = message.toLogList();
				eaiLogUI.setStandardHeader(logItems);
			} catch (Exception e) {
				logger.error("StandardMessageUtil.generateMessageFromDb error",
										e);
				eaiLogUI.setStandardHeader(new ArrayList<>());
				eaiLogUI.setErrStandardHeaderText(header);
			}
		}

		// SUBHEADER
		setSubMessageLog(eaiLogDetailSearch, eaiLogUI);

		String logProcessNo = eaiLogUI.getLogprcssserno();
		String tgtAdapterGroupName ;

		String syncAsyncType = eaiLogUI.getSvcmotivusedstcd();
		if("ASYN".equals(syncAsyncType) && "400".equals(logProcessNo)){
			tgtAdapterGroupName = eaiLogUI.getPsvsysadptrbzwkgroupname();
		} else {
			switch (logProcessNo) {
				case "200":
				case "300":
					tgtAdapterGroupName = eaiLogUI.getPsvsysadptrbzwkgroupname();
					break;
				default:
					tgtAdapterGroupName = eaiLogUI.getGstatsysadptrbzwkgroupname();
					break;
			}
		}

		AdapterGroupUI adapterGroupUI = adapterManService.selectDetail(tgtAdapterGroupName);

		String adapterMessageStdType = adapterGroupUI.getAdptrmsgptrncd();
		String fullMessageData ;
		String dataPart = eaiLogUI.getBzwkdatactnt();
		if("K".equals(adapterMessageStdType) && message != null) {
			String adapterMessageDataType = adapterGroupUI.getAdptrmsgdstcd();
			String charset = adapterGroupUI.getMsgencode();
			message.setBizData(dataPart, charset);
			switch (adapterMessageDataType) {
				case EAITable.JSON_TYPE:
					fullMessageData = message.toJson(true);
					logger.info("data ", fullMessageData.toString());
					break;
				case EAITable.XML_TYPE:
					fullMessageData = message.toXML(charset, true);
					break;
				default:
					fullMessageData = message.toFixedString(charset);
					break;
			}
		}else{
			fullMessageData = dataPart;		
		}
				
		eaiLogUI.setFullmessagedata(fullMessageData);

		return eaiLogUI;
	}

	public String extractDataBetweenPipes(String input) {
	    Pattern pattern = Pattern.compile("\\|(.*?)\\|$");
	    Matcher matcher = pattern.matcher(input);
	    
	    if (matcher.find()) {
	        return matcher.group(1);
	    }
	    
	    return null;
	}
	
	public String extractDataBetweenPipes2(String input) {
	    try {
	        if (StringUtils.isBlank(input)) {
	            return null;
	        }
	        
	        int firstPipeIndex = input.indexOf("|");
	        int lastPipeIndex = input.lastIndexOf("|");
	        
	        if (firstPipeIndex == -1 || lastPipeIndex == -1 || firstPipeIndex == lastPipeIndex) {
	            logger.warn("Invalid input format: No suitable pipes found");
	            return null;
	        }
	        
	        String extractedValue = input.substring(firstPipeIndex + 1, lastPipeIndex).trim();
	        
	       return extractedValue;
	       
	    } catch (Exception e) {
	        logger.error("Error extracting Data between pipes", e);
	        return null;
	    }
	}	
	
	private void setSubMessageLog(EAILogDetailSearch eaiLogSearch,
							EAILogUI eaiLogUI) {

		String searchDate = eaiLogSearch.getMsgDpstYMS();
		String eaisvcserno = eaiLogUI.getEaisvcserno();
		String logprcssserno = eaiLogUI.getLogprcssserno();

		eaiLogUI.setSubHeaderExist("0");
		SubMessageLogId subMessageLogId = new SubMessageLogId();
		subMessageLogId.setEaisvcserno(eaisvcserno);
		subMessageLogId.setLogprcssserno(logprcssserno);

		SubMessageLog subMessageLog = subMessageLogService.getById(searchDate,
								subMessageLogId);

		if (subMessageLog != null && StringUtils
								.isNotBlank(subMessageLog.getHeaderdata())) {
			String subHeader = subMessageLog.getHeaderdata();
			String stdLayout = subMessageLog.getLayoutname();

			try {
				StandardMessage message = StandardMessageUtil
										.generateMessageFromDb(stdLayout);
				StandardReader reader = new FlatReader();
				reader.parse(message, subHeader);
				ArrayList<StandardLogItem> logItems = message.toLogList();
				eaiLogUI.setSubHeader(logItems);
				eaiLogUI.setSubHeaderExist("1");
			} catch (Exception e) {
				logger.error("StandardMessageUtil.generateMessageFromDb error",
										e);
				eaiLogUI.setSubHeader(new ArrayList<>());
				eaiLogUI.setErrSubHeaderText(subHeader);
			}

		}

	}

	public List<Map<String, Object>> selectListToExcel(
							EAILogSearch eaiLogSearch) {
		Page<EAILogUI> page = this.selectList(Pageable.unpaged(), eaiLogSearch);

		return page.getContent().stream().map(ui -> {
			Map<String, Object> map = new HashMap<>();

			map.put("EAISVCSERNO_TMP", ui.getEaisvcserno());
			map.put("KEYMGTMSGCTNT", ui.getKeymgtmsgctnt());
			map.put("LOGPRCSSSERNO_TMP", ui.getLogprcssserno());
			map.put("EAISVCNAME", ui.getEaisvcname());
			map.put("EAISVCDESC", ui.getEaisvcdesc());
			map.put("EAIBZWKDSTCD_TMP", ui.getEaibzwkdstcd());
			map.put("EXTBIZCD", ui.getExtbizcd());
			map.put("REFKEY", ui.getRefkey());
			map.put("MSGDPSTYMS_TMP", ui.getMsgdpstyms());
			map.put("MSGPRCSSYMS", ui.getMsgprcssyms());

			return map;
		}).collect(Collectors.toList());

	}

	public Map<String, List<ComboVo>> selectCombo() {
		List<ComboVo> bizList = comboService.getFromUserBusiness();
		List<ComboVo> extBizList = comboService.getFromExtBusiness();

		List<ComboVo> listLogPrcssSerno = comboService
								.getFromCode("LOGPRCSSSERNO_TYPE");

		Map<String, List<ComboVo>> map = new HashMap<>();
		map.put("eaiBizList", bizList);
		map.put("extBizList", extBizList);
		map.put("logPrcssSernoList", listLogPrcssSerno);

		return map;
	}

	public Map<String, List<ComboVo>> selectDetailCombo() {
		// 업무구분
		List<ComboVo> listBiz = comboService.getFromUserBusiness();
		Map<String, List<ComboVo>> map = new HashMap<>();
		map.put("bizList", listBiz);
		return map;
	}

	public HttpAdapterExtraLogVo selectHttpAdapterExtraLog(String prcsDate,
							String guid, int serviceProcessNumber) {
		HttpAdapterExtraLogId httpAdapterExtraLogId = new HttpAdapterExtraLogId();
		httpAdapterExtraLogId.setPrcsDate(prcsDate);
		httpAdapterExtraLogId.setGuid(guid);
		httpAdapterExtraLogId.setServiceProcessNumber(serviceProcessNumber);

		HttpAdapterExtraLog httpAdapterExtraLog = httpAdapterExtraLogService.getById(httpAdapterExtraLogId);
//		Optional<HttpAdapterExtraLog> optional = httpAdapterExtraLogService
//								.findById(new HttpAdapterExtraLogId(
//										prcsDate, guid, serviceProcessNumber));
//
//		if (optional.isPresent()) {
//			return httpAdapterExtraLogMapper.toVo(optional.get());
//		}

		return httpAdapterExtraLogMapper.toVo(httpAdapterExtraLog);
	}
	
	public String resultByMessage(EAILogDetailSearch paramVo,
			EAILogUI detailData, String type)
			throws Exception {
		return resultByMessageWithLogDetail(paramVo, detailData, type).getResultString();
	}

	public LogBizResultItem resultByMessageWithLogDetail(EAILogDetailSearch paramVo,
							EAILogUI detailData, String type)
							throws Exception {
		EAILogUI data = detailData;
		String result = "";
		LogBizResultItem logBizResultItem = new LogBizResultItem();

		boolean isRequest = true;

		try {

			if (STDMessageKeys.SEND_RECV_CD_RECV.equals(
									data.getTELGMDMNDDSTCD())) {
				isRequest = false;
			}

			// ######################### 변환 정보 추출 추가 2012.05.31 YYJ
			// #########################
			//
			// ASYN - ASYN 거래일 경우 변환 1개 : 100->200 ChngMsgIDName,
			// 300->400 ChngMsgIDName
			// 에러인경우 ErrRspnsChngMsgIDName
			// SYNC - SYNC 거래일 경우 변환 2개 : 100->200 ChngMsgIDName,
			// 300->400
			// BascRspnsChngMsgIDName, 에러인경우 ErrRspnsChngMsgIDName
			// IF 변환 값이 없는 경우 RefMsgIDName 로 설정 ( 변환유무가 없음이면 무조건
			// RefMsgIDName 으로 처리 해야 함 )
			// InptMsgIDName 값 추적해서 정확하게 확인할 것 ! => EAILogDAO 에서
			// refMsgID 값을 INSERT 하고 있음 (
			// 사용 가능 ) 요청,응답,에러
			// 그 외 에러처리

			// EMS의 레이아웃 파싱에서의 정의-파싱룰 순서 2020.05.28
			// 1) 변환 매핑의 레이아웃
			// 2) 로그조회키(필드추출정보)의 레이아웃(레이아웃 부분만 잘라서 사용)
			// 3) 참조메시지ID의 레이아웃

			int logSeq = Integer.parseInt(data.getLogprcssserno());

			if (logger.isDebugEnabled()) {
				logger.debug(String.format("[TELGMDMNDDSTCD]             =[%s]",
										data.getTELGMDMNDDSTCD()));
				logger.debug(String.format("[INPTMSGIDNAME]             =[%s]",
										data.getInptmsgidname()));
				logger.debug(String.format("[LOGPRCSSSERNO]            =[%d]",
										logSeq));
				logger.debug(String.format("[SVCMOTIVUSEDSTCD]         =[%s]",
										data.getSvcmotivusedstcd()));
				logger.debug(String.format(
										"[CHNGMSGIDNAME]            =[%s](%s)",
										data.getChngmsgidname(),
										data.getChngyn()));
				logger.debug(String.format(
										"[BASCRSPNSCHNGMSGIDNAME]   =[%s](%s)",
										data.getBascrspnschngmsgidname(),
										data.getBascrspnschngyn()));
				logger.debug(String.format(
										"[ERRRSPNSCHNGMSGIDNAME]    =[%s](%s)",
										data.getErrrspnschngmsgidname(),
										data.getErrrspnschngyn()));
			}

			TransformVo vo = new TransformVo();
			String cnvsnName = "";
			String[] ref = (data.getInptmsgidname()).split(","); // 참조
																	// 메세지
																	// ID
			int refIndex = 1;

			// 로그조회키(필드추출정보)의 레이아웃 추가
			String logSelectKeyID = "";

			List dataList = fieldExtractInfoService
									.selectDetail(data.getEaisvcname(), "TR");

			if (!dataList.isEmpty()) {
				HashMap tmp = (HashMap) dataList.get(0);
				logSelectKeyID = (String) tmp.get("BZWKFLDNAME");
				logSelectKeyID = logSelectKeyID.substring(0,
										logSelectKeyID.indexOf("."));
			}

			if (logger.isDebugEnabled()) {
				logger.debug(String.format(
										"Layout of log inquiry key (field extraction information) : %s",
										logSelectKeyID));
			}

			// 변환여부(ChngYn)가 1 이면 변환메세지ID명 셋팅
			if ((logSeq == 100 || logSeq == 200)) {

				if (data.getChngyn().equals("1") && !""
										.equals(data.getChngmsgidname())) {
					cnvsnName = data.getChngmsgidname();
				}

				refIndex = 1;

			} else if ((logSeq == 300 || logSeq == 400)) {

				if ("SYNC".equals(data.getSvcmotivusedstcd())) {
					refIndex = 2;

					// 기본 응답전문 인 경우
					if ("1".equals(data.getBascrspnschngyn()) && !"".equals(
											data.getBascrspnschngmsgidname())) {
						cnvsnName = data.getBascrspnschngmsgidname();
					}

					// 출력데이터유형이 02인 경우 AND 에러 응답전문 인 경우
					if ("02".equals(data.getOUTPTDPTRNDSTCD()) && "1"
											.equals(data.getErrrspnschngyn())
											&& !"".equals(data.getErrrspnschngmsgidname())) {
						cnvsnName = data.getErrrspnschngmsgidname();
						refIndex = 3;
					}

				} else { // ASYNC 인 경우
					// ASYN-SYNC 고려

					if ("SYNC".equals(data.getPsvintfacdsticname())
											&& logSeq == 300) {

						refIndex = 2;

						// 기본 응답전문 인 경우
						if ("1".equals(data.getBascrspnschngyn()) && !"".equals(
												data.getBascrspnschngmsgidname())) {
							cnvsnName = data
													.getBascrspnschngmsgidname();
						}

						// 출력데이터유형이 02인 경우 AND 에러 응답전문 인 경우
						if ("02".equals(data.getOUTPTDPTRNDSTCD()) && "1"
												.equals(data.getErrrspnschngyn())
												&& !"".equals(data.getErrrspnschngmsgidname())) {
							cnvsnName = data.getErrrspnschngmsgidname();
							refIndex = 3;
						}

					} else {

						// 서비스 처리번호가 2이면 ASYNC-SYNC 거래일 확률 100%
						if ("2".equals((data.getSvcprcssno())
												.trim())) {

							EAILogDetailSearch eaiLogDetailSearch = new EAILogDetailSearch();
							eaiLogDetailSearch.setEaiBzwkDstcd(
													paramVo.getEaiBzwkDstcd());
							eaiLogDetailSearch.setEaiSvcSerno(
													paramVo.getEaiSvcSerno());
							eaiLogDetailSearch.setLogPrcssSerno("300");
							eaiLogDetailSearch.setMsgDpstYMS(
													paramVo.getMsgDpstYMS());

							EAILogUI asData = selectDetail(eaiLogDetailSearch);

							if ("SYNC".equals(asData.getPsvintfacdsticname())) { // 300
																					// 전문인
																					// SYNC인지
																					// 다시
																					// 한
																					// 번
																					// 확인
								// InptMsgIDName 변경
								data.setInptmsgidname(asData
														.getInptmsgidname());
								ref = asData.getInptmsgidname().split(",");

								refIndex = 2;

								// 기본 응답전문 인 경우
								if ("1".equals(asData.getBascrspnschngyn())
														&& !"".equals(asData.getBascrspnschngmsgidname())) {
									cnvsnName = asData.getBascrspnschngmsgidname();
								}

								// 출력데이터유형이 02인 경우 AND 에러 응답전문 인 경우
								if ("02".equals(data.getOUTPTDPTRNDSTCD())
														&& "1".equals(asData.getErrrspnschngyn())
														&& !"".equals(asData.getErrrspnschngmsgidname())) {
									cnvsnName = asData.getErrrspnschngmsgidname();
									refIndex = 3;
								}

							}

						} // end 2 svcPrcssNo if
						else { // ASYNC-ASYNC
							refIndex = 1;

							if (data.getChngyn().equals("1") && !"".equals(
													data.getChngmsgidname())) {
								cnvsnName = data.getChngmsgidname();
							}

							// 출력데이터유형이 02인 경우 AND 에러 응답전문 인 경우
							if ("02".equals(data.getOUTPTDPTRNDSTCD()) && "1"
													.equals(data.getErrrspnschngyn())
													&& !"".equals(data.getErrrspnschngmsgidname())) {
								cnvsnName = (String) data
														.getErrrspnschngmsgidname();
								refIndex = 3;
							}

						} // end else

					} // end else

				} // end else

			} // end 300 || 400

			if (ref.length >= refIndex) {
				vo.setRefMsgIDNo(refIndex);
				vo.setRefMsgIDName(ref[refIndex - 1].trim());
			}

			// 2. LayoutType 설정
			if (logSeq == 100 || logSeq == 300) {
				vo.setLayoutType(TransformVo.SOURCE_LAYOUT);
			} else {
				vo.setLayoutType(TransformVo.TARGET_LAYOUT);
			}

			// 3. LoutName 설정
			// 변환메세지값으로 소스, 타겟 메세지 값 찾아서 변환 처리 해야 함
			if (logger.isDebugEnabled()) {
				logger.debug(String.format("[ref]            =[%s](%d)", ref[0],
										refIndex));
				logger.debug(String.format("[cnvsnName]      =[%s]",
										cnvsnName));
			}

			// 기본 레이아웃으로 설정
			// vo.setLoutName( cnvsnName );
			if (!"".equals(cnvsnName)) {
				vo.setCnvsnName(cnvsnName);
				TransformEntity transformEntity = transformService
										.getById(cnvsnName);
				List<TransformTypeEntity> transformTypeEntities = transformEntity
										.getTransformTypeEntities();

				if (transformTypeEntities.size() == 2) {

					for (TransformTypeEntity tf : transformTypeEntities) {

						if ((vo.getLayoutType()).equals(tf.getId()
												.getSourcrsultdstcd())) {
							vo.setLoutName(tf.getId().getLoutname());
							break;
						}

					}

				}
				// 변환 레이아웃 값이 셋팅되지 않은 경우
				else if (transformTypeEntities.size() < 2) {

					if (ref.length >= refIndex) {
						cnvsnName = ref[refIndex - 1];
						vo.setLoutName(cnvsnName);
					}

				} else {

					if (vo.getLayoutType().equals(TransformVo.SOURCE_LAYOUT)) {

						for (TransformTypeEntity tf : transformTypeEntities) {

							if (vo.getRefMsgIDName().equals(
													tf.getId().getLoutname())) {
								vo.setLoutName(tf.getId().getLoutname());
								break;
							}

						}

					} else { // 타겟레이아웃일경우

						for (TransformTypeEntity tf : transformTypeEntities) {

							if ((TransformVo.TARGET_LAYOUT).equals(tf.getId()
													.getSourcrsultdstcd())) {
								vo.setLoutName(tf.getId().getLoutname());
								break;
							}

						}

					}

				}

			} else {

				if (!"".equals(logSelectKeyID)) { // 로그조회키(필드추출정보)의
													// 레이아웃이 존재할경우
					vo.setLoutName(logSelectKeyID);
				} else {

					if (ref.length >= refIndex) {
						cnvsnName = ref[refIndex - 1];
						vo.setLoutName(cnvsnName);
					}

				}

				// END OF if( !"".equals(cnvsnName) )
			}

			if (logger.isDebugEnabled()) {
				logger.debug(String.format(
										"[vo.getLoutName()    ]            =[%s]",
										vo.getLoutName()));
			}

			// 4. 변환 레이아웃에서 찾은 값으로 Layout 바인딩
			String detailList = detailData.getBzwkdatactnt();

			if (vo.getLoutName() != null
									&& !"".equals(vo.getLoutName().trim())) {
				int len = detailList.getBytes().length;
				int headerLength = 0;

				// 헤더 추가삭제 로직이 있는 경우 헤더길이만큼 제외한다.
				try {
					String eaiSvcName = detailData.getEaisvcname();
					String svcPrcssNo = detailData.getSvcprcssno();

					ServiceMessageEntity map = serviceMessageService
											.getById(new ServiceMessageEntityId(
																	eaiSvcName,
																	Integer.parseInt(svcPrcssNo)));
					String headerClassName = map.getHdrrefclsname();
					String supplDelYn = map.getSuppldelyn();

					if (HeaderActionKeys.SUPPORT_Y.equals(supplDelYn)
											&& (headerClassName != null
																	&& headerClassName.trim()
																							.length() > 0)) {
						HeaderAction action = HeaderActionFactory
												.createAction(headerClassName);
						headerLength = action.getHeaderLength();
						len = len - headerLength;

						if (logger.isDebugEnabled()) {
							logger.debug(String.format(
													"Header Length Check : %s HeaderLength=%d",
													headerClassName,
													headerLength));
						}

					}

				} catch (Exception ex) {
					logger.warn("Skip Header Length Check : HeaderAction Error",
											ex);
				}

				boolean isEbcdic = false;
				String layoutName = vo.getLoutName();
				LayoutEntity layoutEntity = layoutService.getById(layoutName);
				String layoutTypeName = layoutEntity.getLoutptrnname();
				LayoutTypeManager.getInstance().reload(layoutTypeName);
				Layout layout = layoutJdbcDao.load(layoutEntity);
				Message source = (Message) layout.getRootItem();
				source.createObject();
				source.initOccMin();
				String layoutType = layout.getLayoutType().getName();

				if (logger.isDebugEnabled()) {
					logger.debug(String.format("[LoutType]=[%s]", layoutType));
				}

				if (logger.isInfoEnabled()) {
					logger.info(String.format("[LoutType] =[%s]", layoutType));
					logger.info(String.format(" headerLength : %d",
											headerLength));
				}

				// KB에서는 EBCDIC일 경우 ASCII로 저장된다.
				// 길이가 유지되는 ebcdicToASCIIWithSOSI
				if ("EBCDIC".equals(layoutType)
										|| "COMEBCDIC".equals(layoutType)
										|| "VEBCDIC".equals(layoutType)) {
					isEbcdic = true;
					byte[] asciiBytes = null;

					if (headerLength > 0) {
						len = len - headerLength;
						ByteBuffer bb = ByteBuffer.allocate(len);
						bb.put(detailList.getBytes(), headerLength, len);
						asciiBytes = bb.array();
					} else {
						asciiBytes = detailList.getBytes();
					}

					// SOSI없도록 한다.
					byte[] ebcdicBytes = null;
					ebcdicBytes = IConv.asciiToEBCDICNoSOSI(
											new String(asciiBytes));

					try {
						source.setData(ebcdicBytes);
					} catch (MessageSetDataException e) {
						logger.error("error", e);
					}
					logBizResultItem.setBizLogObjectDetail(source.toString());
					result = source.toXMLString();

				} else if ("BYTES".equals(layoutType)
										|| "VBYTES".equals(layoutType)
										|| "COMBYTES".equals(layoutType)
										|| "VFIELD".equals(layoutType)) {
											byte[] asciiBytes = null;

											if (headerLength > 0) {
												len = len - headerLength;
												ByteBuffer bb = ByteBuffer
																		.allocate(len);
												bb.put(detailList.getBytes(SystemKeys.getBytesMessageCharset()),
																		headerLength,
																		len);
												asciiBytes = bb.array();
											} else {
												asciiBytes = detailList.getBytes(SystemKeys.getBytesMessageCharset());
											}

											try {
												source.setData(asciiBytes);
											} catch (MessageSetDataException e) {
												logger.error("", e);
											}
											logBizResultItem.setBizLogObjectDetail(source.toString());
											result = source.toXMLString();

										} else
					if ("XML".equals(layoutType) || "VXML".equals(layoutType)) {
						String bzwkDataCtnt = detailList; // new
															// String(bb.array());

						if (!bzwkDataCtnt.startsWith("<?xml")) {
							bzwkDataCtnt = XML_DECLARATION
													+ "<" + layout.getName()
													+ " desc=\""
													+ layout.getDescription()
													+ "\">" + detailList + "</"
													+ layout.getName()
													+ ">";
						}

						result = bzwkDataCtnt;

						if ("GRID".equals(type)) {
							String rootItemName = ((XMLMessage) source)
													.getLayout().getRootItem()
													.getName();
							byte[] byteXML = null;
							String xmlStr = "";
							xmlStr = detailList;
							// 중간에 태그가 잘못되어있을수있으니 remove 후 add
							xmlStr = MessageUtil.removeXMLHeader(xmlStr);
							xmlStr = "<" + rootItemName + ">" + xmlStr + "</"
													+ rootItemName + ">";
							xmlStr = MessageUtil.addXMLHeader("utf-8", xmlStr);
							source.setData(xmlStr);
						}

					} else if ("JSON".equals(layoutType)
											|| "UJSON".equals(layoutType)) {
												String bzwkDataCtnt = detailList;

												String rootItemName = layout
																		.getRootItem()
																		.getName();
												JSONObject value = (JSONObject) JSONValue
																		.parse(bzwkDataCtnt);
												JSONObject root = new JSONObject();
												root.put(rootItemName, value);
												String jsonString = root.toJSONString();

												source.setData(jsonString);
												result = source.toXMLString();

											} else
						if ("UXML".equals(layoutType)) {
							String bzwkDataCtnt = detailList;

							if (!bzwkDataCtnt.startsWith("<?xml")) {
								bzwkDataCtnt = XML_DECLARATION
														+ "<" + layout.getName()
														+ " desc=\""
														+ layout.getDescription()
														+ "\">" + detailList
														+ "</"
														+ layout.getName()
														+ ">";
							}

							result = bzwkDataCtnt;

							if ("GRID".equals(type)) {
								String rootItemName = ((XMLMessage) source)
														.getLayout()
														.getRootItem()
														.getName();
								byte[] byteXML = null;
								String xmlStr = "";
								xmlStr = detailList;
								xmlStr = MessageUtil.removeXMLHeader(xmlStr);
								xmlStr = "<" + rootItemName + ">" + xmlStr
														+ "</" + rootItemName
														+ ">";
								source.setData(xmlStr);
							}

						} else if ("DELIMITER".equals(layoutType)) {
							// ByteBuffer bb =
							// ByteBuffer.allocate(len);
							// bb.put(detailList.getBytes(),
							// headerLength, len);
							source.setData(detailList.getBytes());
							result = source.toXMLString();
						} else {
							ByteBuffer bb = ByteBuffer.allocate(len);
							bb.put(detailList.getBytes(), headerLength, len);
							source.setData(bb.array());
							result = source.toXMLString();
						}

				if (!("XML".equals(layoutType) || "UXML".equals(layoutType)
										|| "VXML".equals(layoutType))) {
					// xml 시작 태그 replace
					Matcher m = Pattern.compile("<[0-9]").matcher(result);
					if (m.find())
						result = result
												.replaceAll("<[0-9]", "<_" + m
																		.group()
																		.charAt(1))
												.replaceAll("</[0-9]", "</_" + m
																		.group()
																		.charAt(1));
				}

				// logger.info("source.toXMLString()" +
				// source.toXMLString() ) ;

				if ("GRID".equals(type) || "MEDIA".equals(type)) { // 업무데이터
																	// 그리드로
																	// 보여주기
																	// 추가
					String line_delimiter = "^LINE^";
					StringBuffer dataStr = new StringBuffer();
					String parserErr = null;

					if (source != null) {

						if (source instanceof MonitoringVBytesMessage
												|| source instanceof MonitoringVEbcdicMessage) {
							parserErr = "<font color='#0000FF'>It is not properly parsed. Parsing was done to facilitate error detection.";
						}

						Item root = source;
						ArrayList alist = getItemInfo(root, 0, isEbcdic);

						String itemInfo = "";

						for (int i = 0; i < alist.size(); i++) {
							itemInfo = (String) alist.get(i);

							// ADD Error Header
							if (i == 0 && parserErr != null) {
								String[] errArr = itemInfo.split("\\^FLD\\^");
								dataStr.append(errArr[0] + "^FLD^" + errArr[1]
														+ "^FLD^" + parserErr
														+ "^FLD^ ^FLD^");
								dataStr.append(line_delimiter);
							} else {
								dataStr.append(itemInfo);
								dataStr.append(line_delimiter);
							}

						}

					}

					result = dataStr.toString();

				}

			} else {
				result = XML_DECLARATION
										+ "<MESSAGE>" + "<DATA><![CDATA["
										+ detailList
										+ "\n]]></DATA>" + "</MESSAGE>";

				if ("GRID".equals(type) || "MEDIA".equals(type)) {// 업무데이터,
																	// 입력매체부
																	// 그리드로
																	// 보여주기
																	// 추가
					result = null;
				}

			}

			/////////////////////////////////////////////////////////////////////////////////////////////////
		} catch (Exception e) {
			logger.error("getXMList error", e);

			// Log Cause message
			String msg = null;
			StringBuilder sb = new StringBuilder();

			sb.append(e.getLocalizedMessage()).append("\n");
			Throwable t = e;

			while (t != null) {
				t = t.getCause();
				if (t == null)
					break;
				msg = t.getLocalizedMessage();
				sb.append(msg).append("\n");
			}

			result = XML_DECLARATION + "<ERROR>"
									+ "<MSG>Error during XML conversion - business data and layout do not match.</MSG>"
									+ "<DATA><![CDATA[" + sb.toString()
									+ "\n]]></DATA>" + "</ERROR>";

			if ("GRID".equals(type) || "MEDIA".equals(type)) {// 업무데이터
																// 그리드로
																// 보여주기
																// 추
				result = " ^FLD^ ^FLD^<font color='#0000FF'>It's not parsed normally.Business data and layout are inconsistent.^FLD^ ^FLD^^LINE^";
			}

		}
		
		logBizResultItem.setResultString(result);

		return logBizResultItem;
	}

	public ArrayList getItemInfo(Item sitem, int path, boolean isEbcdic) {
		ArrayList alist = new ArrayList();
		String field_delimiter = "^FLD^";

		StringBuffer sb = new StringBuffer();

		if (sitem.getNodeTypeId() == Item.NODE_MESSAGE
								|| sitem.getNodeTypeId() == Item.NODE_GROUP) {
			sb.append(sitem.getName());
			sb.append(field_delimiter);
			sb.append(sitem.getDescription());
			sb.append(field_delimiter);

			Object obj = sitem.getValue();
			String value = "";

			if (obj == null) {
				value = " ";
			} else if (obj instanceof String) {
				value = (String) obj;
			} else if (obj instanceof byte[]) {
				if (isEbcdic) {
					value = IConv.ebcdicToASCIISOSISpaceEX((byte[]) obj);
				} else {
					try {
						value = new String((byte[]) obj, SystemKeys.getBytesMessageCharset());
					} catch (UnsupportedEncodingException e) {
						value = new String((byte[]) obj);
					}
				}
			} else {
				value = "";
			}

			sb.append(value);

			sb.append(field_delimiter);
			sb.append(" ");

			alist.add(sb.toString());

			List list = sitem.getChildList();
			path++;

			for (int i = 0; i < list.size(); i++) {

				// System.out.println("LOOP ["+path+"]["+i+"]["+
				// ((Item)list.get(i)).getName()
				// +"]");
				if (!((Item) list.get(i)).toString("", false).equals("")) {
					ArrayList slist = getItemInfo((Item) list.get(i), path,
											isEbcdic);

					for (int j = 0; j < slist.size(); j++) {
						alist.add((String) slist.get(j));
					}

				}

			}

		} else {
			sb.append(sitem.getName());
			sb.append(field_delimiter);
			sb.append(sitem.getDescription());
			sb.append(field_delimiter);
			Object obj = sitem.getValue();

			String value = "";

			if (obj == null) {
				value = "NULL";
			} else if (obj instanceof String) {
				value = new String(((String) obj).getBytes());
			} else if (obj instanceof byte[]) {

				if (isEbcdic) {
					value = IConv.ebcdicToASCIISOSISpaceEX((byte[]) obj);
				} else {
					try {
						value = new String((byte[]) obj, SystemKeys.getBytesMessageCharset());
					} catch (UnsupportedEncodingException e) {
						value = new String((byte[]) obj);
					}
				}

			} else if (obj instanceof BigDecimal) {
				value = ((BigDecimal) obj).toPlainString();
			} else if (obj instanceof Integer) {
				value = String.valueOf(obj);
			} else {
				value = obj.toString();
			}

			sb.append(value);

			sb.append(field_delimiter);
			sb.append("" + sitem.getLength());

			alist.add(sb.toString());
		}

		return alist;
	}

}
