package com.eactive.eai.flowcontroller;

import java.util.Properties;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.common.EAIKeys;
import com.eactive.eai.common.EAITable;
import com.eactive.eai.common.exception.ExceptionHandler;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.routing.IFRouter;
import com.eactive.eai.common.routing.Process;
import com.eactive.eai.common.routing.RouteKeys;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.C2RServiceMapper;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.MessageUtil;
import com.eactive.eai.common.util.ObjectUtil;
import com.eactive.eai.common.util.ServiceUtil;
import com.eactive.eai.control.LogSender;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.service.InterfaceMapper;
import com.eactive.eai.transformer.function.util.CodeConversion;
import com.ext.eai.common.stdmessage.STDMessageKeys;

public class C2RFCProcess extends Process {
	public String adptrMsgPtrnCd;

	public java.lang.String adapterGroupName;

	public int logPssSeq;

	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

	public java.util.Properties callProp;

	public EAIMessage resEaiMsg;

	public com.eactive.eai.common.message.EAIMessage reqEaiMsg;

	static Logger esbLogger = Logger.getLogger(Logger.LOGGER_ESBFW);
	private int svrLogLevel;
	private static ThreadLocal local = new ThreadLocal(); // 경과시간 동기화를 위한 ThreadLocal

	static final long serialVersionUID = 1L;

	String guidLogPrefix = "";

	public C2RFCProcess() {

	}

	public EAIMessage clientReturn() {
		if (svrLogLevel >= 1) {
			try {
				long msgSndTm = System.currentTimeMillis();

				Long threadData = (Long) local.get();
				long processTm = msgSndTm - threadData.longValue();
				if (esbLogger.isInfo())
					esbLogger.info(guidLogPrefix + "  End... - elapse time : " + processTm
							+ "(ms) - EAI Server Log Level is INFO");
			} catch (Exception e) {
			}
		}
		return resEaiMsg;
	}

	public void callService(com.eactive.eai.common.message.EAIMessage reqEaiMsg, java.util.Properties callProp) {
		this.reqEaiMsg = reqEaiMsg;
		this.callProp = callProp;

		if (this.reqEaiMsg == null)
			return;

		svrLogLevel = this.reqEaiMsg.getSvrLogLvl();

		guidLogPrefix = "B2BFCProcess] GUID[" + getGuid(reqEaiMsg) + "] UUID[" + this.reqEaiMsg.getSvcOgNo() + "] ";

		if (svrLogLevel >= 1) {
			try {
				long msgRcvTm = System.currentTimeMillis();
				local.set(new Long(msgRcvTm));
				if (esbLogger.isInfo())
					esbLogger.info(guidLogPrefix + " started... - EAI Server Log Level is INFO");
			} catch (Exception e) {
			}
		}
		try {
			flow();
		} catch (Exception e) {
			printExceptionMessage(e);
			if (isSourceAsync()) {
				logSenderCtrlSend();
				callExceptionHandler();
			}
			return;
		}
	}

	public void flow() throws Exception {
		// isRealCall
		if (!isRealCall()) {
			return;
		}
		// 초기화
		init();
		// B2BOutboud Mapping
		setRoute();

		// Outbound 프로세스 호출
		ifRouterProcess();

		// GUID설정
		setGUID();

		// ASYNC-SYNC모델
		if (checkServiceMsgSize()) {
			// ServiceMessage설정
			setCurrentService();
			// 응답메시지전송
			ifRouterProcess1();
		}
		clientReturn();
	}

	public void ifRouterProcess() throws Exception {
		try {
			// call by ref 문제로 deepCopy로 해결 - 200 변환에 의해 변경되는 문제
			EAIMessage reqMsg = (EAIMessage) ObjectUtil.deepCopy(this.reqEaiMsg);
			this.resEaiMsg = IFRouter.process(reqMsg, this.callProp);
		} catch (Exception e) {
			String[] msgArgs = new String[1];
			msgArgs[0] = this.reqEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR001";
			String rspErrorMsg = ExceptionUtil.make(e, rspErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " " + rspErrorMsg);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			throw new Exception(rspErrorCode);
		}

		// ----------------------------------------------------
		// 기동시스템이 Async 거래의 OUtbound에서 오류가 발생할 경우
		// ExceptionHandler를 통해 응답메시지를 전송한다.
		// ----------------------------------------------------
		if (EAIMessageKeys.ASYNC_SVC.equals(this.reqEaiMsg.getSvcTsmtUsgTp())) {
			if (!MessageUtil.checkRspErrCd(this.resEaiMsg.getRspErrCd())) {
				try {
					// 변경없이 요청 EAI메시지를 전달하도록 수정
					String bizData = this.reqEaiMsg.getStandardMessage().getBizData();
					try {
						String inboundCharset = callProp.getProperty(com.eactive.eai.adapter.Keys.CHARSET_KEY_IN);
						this.resEaiMsg.getStandardMessage().setBizData(bizData, inboundCharset);
					} catch (Exception e) {
						logger.error("setBizData error", e);
					}

					ExceptionHandler.handle(this.resEaiMsg);
				} catch (Exception e) {
					if (logger.isError())
						logger.error(guidLogPrefix + " ExceptionHandler Call Error :" + e.getMessage());
				}
			}
		}
	}

	public void ifRouterProcess1() throws Exception {
		try {
			// 신보/기보 때문에 추가함 2013-01-03
			// AS거래에서 처리결과수신서비스ID를 서비스ID로 넣어준다.
			// this.resEaiMsg.getStdMsg().getSTDCommon().setSrvcId(this.resEaiMsg.getStdMsg().getSTDCommon().getProcsRsltRcmsSrvcId());
			this.resEaiMsg = IFRouter.process(this.resEaiMsg, this.callProp);
		} catch (Exception e) {
			String[] msgArgs = new String[1];
			msgArgs[0] = this.reqEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR100";
			String orgErrorCode = ExceptionUtil.getErrorCode(e, rspErrorCode);
			String rspErrorMsg = ExceptionUtil.make(rspErrorCode, orgErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " " + rspErrorMsg);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			// AS 응답송신 중 에러가 밸생할 경우 400 에러로그를 남기도록 수정
			this.logPssSeq = 400;
			LogSender.send(this.logPssSeq, this.resEaiMsg, null);
		}
	}

	public void logSenderCtrlSend() {
		String returnType = getReturnType(reqEaiMsg); // S|R

		if (STDMessageKeys.SEND_RECV_CD_RECV.equals(returnType)) {
			this.logPssSeq = 400;
		} else {
			this.logPssSeq = 200;
		}

		try {
			LogSender.send(this.logPssSeq, this.resEaiMsg, null);
		} catch (Exception e) {
			if (logger.isError())
				logger.error(guidLogPrefix + " AA Exception Logging Error :" + e.getMessage());
		}
	}

	public boolean checkServiceMsgSize() {
		// Outbound Error일 경우
		if (MessageUtil.checkRspErrCd(this.resEaiMsg.getRspErrCd()) && reqEaiMsg.getSvcMsgs().size() > 1) {
			return true;
		} else {
			return false;
		}
	}

	public void setCurrentService() throws Exception {
		if (logger.isDebug())
			logger.debug(guidLogPrefix + " ASYNC-SYNC CALLED");
		
		// AS의 경우 다음 서비스메시지(AS 응답)에 키관리메시지내용을 복사한다.
		String keyMgtMsgVl = resEaiMsg.getCurrentSvcMsg().getKeyMgtMsgVl();
		if (logger.isDebug())
			logger.debug(guidLogPrefix + " AS Response keyMgtMsgVl[" + keyMgtMsgVl + "]");
		resEaiMsg.setSvcPssSeq(resEaiMsg.getSvcPssSeq() + 1);
		resEaiMsg.getCurrentSvcMsg().setKeyMgtMsgVl(keyMgtMsgVl);

		// AS 응답거래 라우팅
		try {
			String srvcId = getServiceId(this.resEaiMsg); // 서비스ID
			String psvItfTp = this.resEaiMsg.getCurrentSvcMsg().getPsvItfTp(); // 수동 호출방식
			if (srvcId == null || srvcId.trim().length() == 0 || srvcId.trim().length() <= 3) {
				if (logger.isDebug())
					logger.debug(guidLogPrefix + " STDMessage ServiceId not found");
			} else {
//        		String reqSysCode = this.resEaiMsg.getExtMsg().getFullHeaderString("systemHeader.DMND_SYS_CD"); // 요청시스템코드(서비스ID 앞의 3자리 시용에서 요청시스템코드로 변경함)
				String reqSysCode = getReqSysCode(this.resEaiMsg);
				String type = "";
				if (EAIMessageKeys.ASYNC_SVC.equals(psvItfTp)) {
					type = "A";
				} else {
					type = "S";
				}
				String key = reqSysCode + type;

				if (logger.isDebug())
					logger.debug(guidLogPrefix + " C2R RouteKey STDMessage ServiceID [" + srvcId + "]");
				if (logger.isDebug())
					logger.debug(
							guidLogPrefix + " C2R RouteKey STDMessage systemHeader.DMND_SYS_CD [" + reqSysCode + "]");
				if (logger.isDebug())
					logger.debug(guidLogPrefix + " C2R RouteKey STDMessage Sync/Async [" + type + "]");
				EAIMessage mappedEaiMsg = C2RServiceMapper.setC2RService(key, this.resEaiMsg);
				if (mappedEaiMsg != null) {
					if (logger.isDebug())
						logger.debug(guidLogPrefix + " EAISVCCD[" + this.resEaiMsg.getEAISvcCd() + "] C2R RouteKey ["
								+ key + "] ServiceMessage mapping");
					this.resEaiMsg = mappedEaiMsg;
				}
			}
		} catch (Exception e) {
			if (logger.isError())
				logger.error(guidLogPrefix + " AS Response : B2B Serice Routing Set Error :" + e.getMessage());
			String[] msgArgs = new String[1];
			msgArgs[0] = this.resEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR002";
			String rspErrorMsg = ExceptionUtil.make(e, rspErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " AS Response : " + rspErrorMsg);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			throw new Exception(rspErrorCode);
		}

		// 기동이 표준인 경우만
		if (com.eactive.eai.adapter.Keys.IF_STANDARD.equals(this.reqEaiMsg.getStdMsgUsgCls())) {
//        	resEaiMsg = ExtMessageConverter.setAsyncSyncResponseForStandard(guidLogPrefix, reqEaiMsg, resEaiMsg);
			setReturnType(resEaiMsg, STDMessageKeys.SEND_RECV_CD_RECV);
		}
	}

	public boolean isOutboundCall() {
		return true;
	}

	public void init() throws Exception {
		try {
			InterfaceMapper mapper = this.reqEaiMsg.getMapper();
			StandardMessage standardMessage = this.reqEaiMsg.getStandardMessage();
			String guid = getGuid(reqEaiMsg);
			String guidseq = getGuidSeq(reqEaiMsg);

			if (logger.isDebug())
				logger.debug(guidLogPrefix + " REQ Current GUID [" + guid + "][" + guidseq + "]");
			mapper.nextGuidSeq(standardMessage);
			guidseq = getGuidSeq(reqEaiMsg);
			if (logger.isDebug())
				logger.debug(guidLogPrefix + " REQ ADD PATHSEQ GUID [" + guid + "][" + guidseq + "]");
			this.resEaiMsg = this.reqEaiMsg;
		} catch (Exception e) {
			String[] msgArgs = new String[1];
			msgArgs[0] = this.reqEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR003";
			String rspErrorMsg = ExceptionUtil.make(e, rspErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " " + rspErrorMsg, e);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			throw new Exception(rspErrorCode);
		}
	}

	public void printExceptionMessage(Exception e) {
		if (logger.isError())
			logger.error(guidLogPrefix + " ExceptionPath : " + e.getMessage());
	}

	public boolean isSourceAsync() {
		if (EAIMessageKeys.ASYNC_SVC.equals(this.reqEaiMsg.getSvcTsmtUsgTp())) {
			return true;
		} else {
			return false;
		}

	}

	public void callExceptionHandler() {
		try {
			AdapterManager adapterManager = AdapterManager.getInstance();
			AdapterGroupVO inAdptGrpVO = adapterManager.getAdapterGroupVO(this.reqEaiMsg.getSngSysItfTp());
			String inAdapterMsgType = inAdptGrpVO.getMessageType();
			if (EAITable.EBCDIC_TYPE.equals(inAdapterMsgType)) {
				String bizData = this.reqEaiMsg.getStandardMessage().getBizData();
				byte[] asc = CodeConversion.meToMixedAsc(bizData.getBytes());
				try {
					String inboundCharset = callProp.getProperty(com.eactive.eai.adapter.Keys.CHARSET_KEY_IN);
					this.reqEaiMsg.getStandardMessage().setBizData(new String(asc), inboundCharset);
				} catch (Exception e) {
					logger.error("setBizData error", e);
				}
			}
			ExceptionHandler.handle(this.reqEaiMsg);
		} catch (Exception e) {
			if (logger.isError())
				logger.error(guidLogPrefix + " ExceptionHandler Call Error :" + e.getMessage());
		}
	}

	public boolean isRealCall() {
		if (this.callProp != null) {
			String mode = this.callProp.getProperty(EAIKeys.CALL_MODE_KEY);
			if (mode != null && EAIKeys.CALL_MODE_WARMUP.equals(mode)) {
				// System.out.println(guidLogPrefix + " Warming Up Called");
				return false;
			} else {
				return true;
			}
		} else {
			return true;
		}
	}

	public void setRoute() throws Exception {
		try {
			String srvcId = getServiceId(this.reqEaiMsg);
			String psvItfTp = this.reqEaiMsg.getCurrentSvcMsg().getPsvItfTp(); // 수동 호출방식

			if (srvcId == null || srvcId.trim().length() == 0 || srvcId.trim().length() <= 3) {
				if (logger.isDebug())
					logger.debug(guidLogPrefix + " STDMessage ServiceID not found");
			} else {
//        		String reqSysCode = this.resEaiMsg.getExtMsg().getFullHeaderString("systemHeader.DMND_SYS_CD"); // 요청시스템코드(서비스ID 앞의 3자리 시용에서 요청시스템코드로 변경함)
				String reqSysCode = getReqSysCode(this.resEaiMsg);
				String type = "";
				if (EAIMessageKeys.ASYNC_SVC.equals(psvItfTp)) {
					type = "A";
				} else {
					type = "S";
				}
				String key = reqSysCode + type;

				if (logger.isDebug())
					logger.debug(guidLogPrefix + " C2R RouteKey STDMessage ServiceID [" + srvcId + "]");
				if (logger.isDebug())
					logger.debug(
							guidLogPrefix + " C2R RouteKey STDMessage systemHeader.DMND_SYS_CD [" + reqSysCode + "]");
				if (logger.isDebug())
					logger.debug(guidLogPrefix + " C2R RouteKey STDMessage Sync/Async [" + type + "]");

				EAIMessage mappedEaiMsg = C2RServiceMapper.setC2RService(key, this.reqEaiMsg);
				if (mappedEaiMsg != null) {
					if (logger.isDebug())
						logger.debug(guidLogPrefix + " EAISVCCD[" + this.reqEaiMsg.getEAISvcCd() + "] C2R RouteKey ["
								+ key + "] ServiceMessage mapping");
					this.reqEaiMsg = mappedEaiMsg;
				}
			}

		} catch (Exception e) {
			// TO-DO
			if (logger.isError())
				logger.error(guidLogPrefix + " B2B Serice Routing Set Error :" + e.getMessage());
			String[] msgArgs = new String[1];
			msgArgs[0] = this.reqEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR002";
			String rspErrorMsg = ExceptionUtil.make(e, rspErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " " + rspErrorMsg);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			throw new Exception(rspErrorCode);
		}

		try {
			this.adapterGroupName = reqEaiMsg.getCurrentSvcMsg().getPsvSysItfTp();
			
			EAIServerManager eaiServerManager = EAIServerManager.getInstance();
			if (eaiServerManager.isTASEnabledEAIServer()
					&& EAIMessageKeys.TRANTYPE_TAS.equals(reqEaiMsg.getTranType())) {
//	        	this.adptrMsgPtrnCd = com.eactive.eai.adapter.Keys.IF_NONSTANDARD;
				PropManager manager = PropManager.getInstance();
				Properties prop = manager.getProperties(RouteKeys.SIM);

				if (reqEaiMsg.getCurrentSvcMsg().isSyncItfTp()) {
					this.adapterGroupName = prop.getProperty(RouteKeys.SIM_ADAPTER_SYNC, "_SIM_OU_HTT_SyC");
				} else {
					this.adapterGroupName = prop.getProperty(RouteKeys.SIM_ADAPTER_ASYNC, "_SIM_OU_HTT_AsC");
				}
			} else {
				AdapterManager adapterManager = AdapterManager.getInstance();
				AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(this.adapterGroupName);

				// 등록된 어댑터가 실제 존재하지 않는 경우 : 2009.04.03
				if (adptGrpVO == null) {
					throw new Exception("수동어댑터[" + this.adapterGroupName + "]가 사용 중이 아님");
				}
				this.adptrMsgPtrnCd = adptGrpVO.getAdptrMsgPtrnCd();
			}
		} catch (Exception e) {
			String[] msgArgs = new String[1];
			msgArgs[0] = this.reqEaiMsg.getEAISvcCd();
			String rspErrorCode = "RECEAIFBR003";
			String rspErrorMsg = ExceptionUtil.make(e, rspErrorCode, msgArgs);
			if (logger.isError())
				logger.error(guidLogPrefix + " " + rspErrorMsg, e);
//			this.resEaiMsg.setRspErrCd(rspErrorCode);
//			this.resEaiMsg.setRspErrMsg(rspErrorMsg);
			this.resEaiMsg.setRspErr(rspErrorCode,rspErrorMsg);
			throw new Exception(rspErrorCode);
		}

		// 2009.03.11
		// AA가 아니고 기동이 표준이 아닌 경우
		// Adapter의 timeout값을 설정한다.
		try {
			String svcTsmtUsgTp = this.reqEaiMsg.getSvcTsmtUsgTp(); // 기동 호출방식
			String psvItfTp = this.reqEaiMsg.getCurrentSvcMsg().getPsvItfTp(); // 수동 호출방식

			AdapterManager adapterManager = AdapterManager.getInstance();
			AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(this.adapterGroupName);

			if (!(EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp) && EAIMessageKeys.ASYNC_SVC.equals(psvItfTp))
					&& !(com.eactive.eai.adapter.Keys.IF_STANDARD.equals(this.reqEaiMsg.getStdMsgUsgCls()))) {
				// SA 또는 SNA Adapter일 경우에 는 SA Timeout값을 설정
				if ((EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) && EAIMessageKeys.ASYNC_SVC.equals(psvItfTp))
						|| Keys.TYPE_SNA.equals(adptGrpVO.getType())) {
					int saTimeoutSec = this.reqEaiMsg.getCurrentSvcMsg().getTmoVl();
					if (logger.isDebug())
						logger.debug("BaseFCProcess] SA TimeoutValue [" + saTimeoutSec + "]");
					setTimeout(this.reqEaiMsg, "" + saTimeoutSec);
				} else {
					int timeout = ServiceUtil.getTimeoutValue(adapterGroupName);
					if (logger.isDebug())
						logger.debug("BaseFCProcess] Target Adapter TimeoutValue [" + timeout + "]");
					if (timeout > 0) {
						if (logger.isDebug())
							logger.debug("BaseFCProcess] Set Adapter TimeoutValue [" + timeout + "] to STDMessage");
						setTimeout(this.reqEaiMsg, "" + timeout);
					}
				}
			}
		} catch (Exception e) {
			if (logger.isWarn()) {
				logger.warn("BaseFCProcess] Get Adapter TimeoutValue Error - " + e.getMessage(), e);
			}
		}
	}

	public void setGUID() throws Exception {
		InterfaceMapper mapper = this.resEaiMsg.getMapper();
		StandardMessage standardMessage = this.resEaiMsg.getStandardMessage();
		String guid = mapper.getGuid(standardMessage);
		String guidseq = mapper.getGuidSeq(standardMessage);

		if (logger.isDebug())
			logger.debug(guidLogPrefix + " REQ Current GUID [" + guid + "][" + guidseq + "]");
		mapper.nextGuidSeq(standardMessage);
		guidseq = mapper.getGuidSeq(standardMessage);
		if (logger.isDebug())
			logger.debug(guidLogPrefix + " REQ ADD PATHSEQ GUID [" + guid + "][" + guidseq + "]");

		String svcTsmtUsgTp = this.reqEaiMsg.getSvcTsmtUsgTp(); // 기동 호출방식
		String psvItfTp = this.reqEaiMsg.getCurrentSvcMsg().getPsvItfTp(); // 수동 호출방식

		// ------------------------------------------------------
		// AA거래 가 아니고 (SS, AS, SA 일 때 모두 처리한다)
		// 기동이 KB내부표준이고
		// 원거래 복원여부가 ‘1’인 경우에
		// => 원거래 GUID를 EAI에서 기동에서 수신된 원거래 GUID로 복원한다.
		// ------------------------------------------------------
		if (!(EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp) && EAIMessageKeys.ASYNC_SVC.equals(psvItfTp))
				&& (com.eactive.eai.adapter.Keys.IF_STANDARD.equals(this.reqEaiMsg.getStdMsgUsgCls()))
				&& (STDMessageKeys.RECOVER_YN_YES.equals(getRecoverYn(this.reqEaiMsg)) ) ) {
			String reqOgtranGuIdNo = getOrgGuid(this.reqEaiMsg);
			mapper.setOrgGuid(standardMessage, reqOgtranGuIdNo);
			if (logger.isInfo()) {
				logger.info(guidLogPrefix + " OgtranGuIdNo 복원 " + this.reqEaiMsg.getSvcOgNo() + " OgtranGuIdNo ["
						+ mapper.getOrgGuid(standardMessage) + "]");
			}
		}
	}
}
