package com.eactive.eai.adapter.socket2.protocol.inisafenatn.codec;

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.config.SocketAdapterContext;
import com.eactive.eai.adapter.socket2.config.SocketLoggerManager;
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.eactive.eai.adapter.socket2.protocol.inisafenatn.handshake.InisafeNatnPayloadFilter;
import com.eactive.eai.adapter.socket2.protocol.inisafenatn.handshake.InisafeNatnPayloadFilter.FilterState;
import com.eactive.eai.common.util.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

public class InisafeNatnPayloadEncoder implements ProtocolEncoder {	
	final protected Logger logger;
    final protected Logger errLogger;

	SocketAdapterContext context;
	ISocketAdapter adapter;
	
	public InisafeNatnPayloadEncoder(ISocketAdapter adapter) {
		this.adapter = adapter;
		this.context = adapter.getContext();

	    logger    = SocketLoggerManager.getInstance().getLogger(context.getAdapterGroupName());
	    errLogger = SocketLoggerManager.getInstance().getErrLogger();
	}

	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
		ByteMessage m = null;
		if (message == null) {
			throw new Exception("cannot write null message.");
		} else if (message instanceof ByteMessage) {
			m = (ByteMessage) message;
		} else if (message instanceof String) {
			m = new ByteMessage((String) message);
		} else if (message instanceof byte[]) {
			m = new ByteMessage((byte[]) message);
		} else {
			throw new Exception("cannot write unknow format message.");
		}
		
		boolean isHandshakeState = false;
		FilterState filterState = (FilterState) session.getAttribute(InisafeNatnPayloadFilter.FILTER_STATE_KEY);
		if (filterState != null && filterState.state != InisafeNatnPayloadFilter.State.STATE_MESSAGE) {
			isHandshakeState = true;
		}
		
		if (isHandshakeState) {
			encodeHandshake(session, m, out);
		} else {
			encodeMessage(session, m, out);
		}
	}
	
	
	private void encodeHandshake(IoSession session, ByteMessage m, ProtocolEncoderOutput out) throws Exception {
		int length = m.size();
		int totalLength = InisafeNatnPayloadConstants.COMMON_LENGTH + length;
		
		ByteBuffer buffer = ByteBuffer.allocate(totalLength).limit(totalLength);

        /**
         * [1] common ll
         */
		String commonLL = CommonLib.getFormatString(totalLength-InisafeNatnPayloadConstants.COMMON_LL_LENGTH, InisafeNatnPayloadConstants.COMMON_LL_LENGTH);
        buffer.put(commonLL.getBytes());                	// 1 전문송신 Byte 수 4 N * 비고 1) 참조

        /**
         * [2] common rest
         */
        buffer.put(getTransactionType(session));            // 2 거래구분코드 	9 A
		buffer.put("00".getBytes());                   		// 3 업무코드       	2 A * Space
		buffer.put("00".getBytes());                  		// 4 거래종류코드 	2 A * Space
		buffer.put(" ".getBytes());			              	// 5 요청응답구분코드 	1 A * Space
		buffer.put("   ".getBytes());                  		// 6 송신기관코드 	3 A * Space
		buffer.put("   ".getBytes());                  		// 7 수신기관코드 	3 A * Space
		buffer.put("        ".getBytes());  				// 8 거래일자 		8 N * Space
		buffer.put("      ".getBytes());        				// 9 거래시각 		6 N * Space
		buffer.put("       ".getBytes());  					// 10 전문관리번호 	7 N * Space
		buffer.put("   ".getBytes());                  		// 11 처리기관코드 	3 N * Space
		buffer.put("    ".getBytes());                  	// 12 처리결과코드 	4 A * Space
		buffer.put(" ".getBytes());                     	// 13 코드변환구분 	1 A * Space
		buffer.put(StringUtils.leftPad("", 61).getBytes()); // 14 공란1 		61 A * Space
		buffer.put(StringUtils.leftPad("", 20).getBytes()); // 15 사용자ID 	20 A * Space
		buffer.put("        ".getBytes());      			// 16 Password 	8 A * Space
		
		/**
		 * [3] individual part
		 */
		buffer.put(m.getBytes());
        
        /**
         * write
         */
        int mark = buffer.position();
        buffer.position(0);
		if (context.getTraceLevel() >= context.TRACE_LEVEL_TRACE) {
			logger.debug(context.getAdapterName() + " | InisafeNatnPayloadEncoder | encode (handshake) : " + CommonLib.hexDump(buffer, context));
			adapter.addRecentSendTrace(session, "InisafeNatnPayloadEncoder | encode (handshake) : " + CommonLib.hexDump(buffer, context));
		}
        buffer.position(mark);
        buffer.flip();
		out.write(buffer);
	}

	
	private byte[] getTransactionType(IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(InisafeNatnPayloadFilter.FILTER_STATE_KEY);
		if (filterState == null) {
			throw new Exception("session does not have filter state");
		}

		switch (filterState.state) {
		case STATE_INIT :
			if (adapter.getContext().isClient()) {
				return "000000001".getBytes();
			} else {
				return "000000002".getBytes();
			}
		case STATE_UPDATE :
			if (adapter.getContext().isClient()) {
				return "000000003".getBytes();
			} else {
				return "000000004".getBytes();
			}
		case STATE_FINAL :
			return "000000005".getBytes();
		default :
			throw new Exception("invalid state");			
		}		
	}

	private void encodeMessage(IoSession session, ByteMessage m, ProtocolEncoderOutput out) throws Exception {
		/**
		 * copy byte[] to buffer
		 */
		int length = m.size();	
		//ByteBuffer buffer = ByteBuffer.allocate(length).limit(length);
		ByteBuffer buffer = ByteBuffer.allocate(context.getLlFieldLength() + length).limit(context.getLlFieldLength() + length);
		
		/**
		 * ll.include = 'Y'  -  길이필드 붙이기
		 * BOK - payload 사이즈 ll필드를 붙이는 기관이 존재
		 */
//		if ("Y".equals(context.getLlInclude())) {
//    		String lenStr = CommonLib.getFormatString(context.getSendPayloadLength(), context.getLlFieldLength());
//    		buffer.put(lenStr.getBytes());
//		}
		if(context.getLlFieldLength() > 0){
    		String lenStr = CommonLib.getFormatString(context.getSendPayloadLength(), context.getLlFieldLength());
    		buffer.put(lenStr.getBytes());
		}
		
		buffer.put(m.getBytes());
        
        /**
         * write
         */
        int mark = buffer.position();
        buffer.position(0);
		if (context.getTraceLevel() >= context.TRACE_LEVEL_TRACE) {
			logger.debug(context.getAdapterName() + " | InisafeEncoder | encode : " + CommonLib.hexDump(buffer, context));
			adapter.addRecentSendTrace(session, "InisafeEncoder | encode : " + CommonLib.hexDump(buffer, context));
		}
        buffer.position(mark);
        buffer.flip();
		out.write(buffer);
	}

	
	public void dispose(IoSession session) throws Exception {
	}
}
