package com.eactive.eai.adapter.socket2.protocol.dynamicoffset.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.common.util.Logger;
import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.util.ByteCharSequence;
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;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

	SocketAdapterContext context;
	ISocketAdapter adapter;
	
	public DynamicOffsetEncoder(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.");
		}				
		
		int offset = getPatternOffset(m.getBytes());
		if (offset == context.getLlFieldOffset()){
			m = addData(m);
		}
		
		if(m == null) {
			throw new Exception("ByteMessage cannot NULL.");
		}
		/**
		 * ll.include = 'Y'  -  통째로 보낸다.
		 */
		if ("Y".equals(context.getLlInclude())) {
			ByteBuffer buffer = ByteBuffer.allocate(m.size() - context.getLlFieldLength());
//			buffer.put(m.getBytes(0, context.getLlFieldOffset()));
//			buffer.put(m.getBytes(context.getLlFieldOffset() + context.getLlFieldLength()));
			buffer.put(m.getBytes(0, offset));
			buffer.put(m.getBytes(offset + context.getLlFieldLength()));
			buffer.flip();
			m.setByteBuffer(buffer);
		}

		int length = m.size();
		ByteBuffer buffer = ByteBuffer.allocate(context.getLlFieldLength() + length).limit(context.getLlFieldLength() + length);

		/**
		 * ll.field.include = 'Y'  -  header 길이 포함하여 ll 을 계산한다.
		 */
		if ("Y".equals(context.getLlFieldInclude())) {
			length = length + context.getLlFieldLength();
		}

		
		/**
		 * ll.offset.exclude = 'Y'  -  offset 만큼의 크기를 빼서 length를 계산한다.
		 */
		if ("Y".equals(context.getLlOffsetExclude())) {
//			length = length - context.getLlFieldOffset();
			length = length - offset;
		}
		
		/**
		 * ll.length.adjust = 200 - 공통부 header 길이를 더한다. (한은망 평문 테스트용)
		 */
		if (context.getLlLenAdjust() != 0) {
			length = length + context.getLlLenAdjust();
		}
		
		
        /**
         * [1] before-payload
         */
//        buffer.put(m.getBytes(0, context.getLlFieldOffset()));
        buffer.put(m.getBytes(0, offset));
		
		/**
		 * [2] write header
		 */
    	if ("CHAR".equals(context.getLlFieldType())) {
    		String lenStr = CommonLib.getFormatString(length, context.getLlFieldLength());
    		buffer.put(lenStr.getBytes());
    	}
		
        /**
         * [3] after-payload
         */
//        buffer.put(m.getBytes(context.getLlFieldOffset(), m.getBytes().length - context.getLlFieldOffset()));
        buffer.put(m.getBytes(offset, m.getBytes().length - offset));
        
        /**
         * write
         */
        int mark = buffer.position();
        buffer.position(0);
        
		if ( context.getTraceLevel() >= context.TRACE_LEVEL_TRACE ) {
			logger.debug(context.getAdapterName() + " | FixedHeaderEncoder | encode : " + CommonLib.hexDump(buffer, context));
			adapter.addRecentSendTrace(session, "FixedHeaderEncoder | encode : " + CommonLib.hexDump(buffer, context));
		}
        buffer.position(mark);
        buffer.flip();
        out.write(buffer);
        out.flush();
	}

	public void dispose(IoSession session) throws Exception {
		// empty
	}
	private int getPatternOffset(byte[] data){
		int offset = 0;
		
    	String dynamicOffsetPattern = context.getDynamicOffsetPattern().trim();
    	Matcher matcher = Pattern.compile(dynamicOffsetPattern).matcher(new ByteCharSequence(data));
    	if(matcher.find()){
    		offset = matcher.start();
    	}else{
    		offset = context.getLlFieldOffset();
    	}
    	return offset;
	}
	private ByteMessage addData(ByteMessage m) throws Exception{
		ByteMessage mm = null ;
		
		/**
		 * adddata.rule
		 */
		if (!"".equals(context.getDataAddRule())) {
			Matcher matcher = Pattern.compile("\\[([ ]*[0-9]+)[ ]*,([ ]*[0-9]+)[ ]*\\][ ]*=[ ]*(.+)").matcher(context.getDataAddRule().trim());
			if (matcher.find()) {
			    int offset = Integer.parseInt(matcher.group(1).trim());
			    int length = Integer.parseInt(matcher.group(2).trim());
			    String re = matcher.group(3).trim();
			    if (re != null && length != re.length()){
			    	re = StringUtil.stringFormat(re, false, ' ', length);
			    }
			    if (re != null){
					mm = (new ByteMessage())
	                .append(m.getBytes(0, offset))
	                .append(re.getBytes())
	                .append(m.getBytes(offset));

			    }
			} else {
				throw new Exception(context.getAdapterName() + " | data.add.rule format is invalid : " + context.getDataAddRule());
			}
		}	
		return mm;
	}
}
