package com.eactive.eai.adapter.socket2.filter.initmsgshake;

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.common.SimpleFuture;
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 org.apache.mina.common.IoFilterAdapter;
import org.apache.mina.common.IoSession;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class InitMsgFilter extends IoFilterAdapter {
	final protected Logger logger;
	final protected Logger errLogger;
	
	private static final String FILTER_STATE_KEY = InitMsgFilter.class.getName() + ".STATE";
	private static class FilterState {
		SimpleFuture initMsgResponseFuture = new SimpleFuture();
	}
	protected SocketAdapterContext context;
	protected ISocketAdapter adapter;
	protected ExecutorService executor;
	private static String endHandshake="N";
	
	public InitMsgFilter(ISocketAdapter adapter) {
		this.adapter = adapter;
		this.context = adapter.getContext();
		
		logger    = SocketLoggerManager.getInstance().getLogger(context.getAdapterGroupName());
		errLogger = SocketLoggerManager.getInstance().getErrLogger();
	}
	
	@Override
	public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		if (filterState == null) {
			filterState = new FilterState();
			session.setAttribute(FILTER_STATE_KEY, filterState);
		}
		nextFilter.sessionCreated(session);
	}
	
	@Override
	public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		if (filterState != null && filterState.initMsgResponseFuture != null) {
			filterState.initMsgResponseFuture.set(new Exception("session closed by peer"));
		}
		endHandshake="N";
		nextFilter.sessionClosed(session);
	}
	
	@Override
	public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		if (filterState != null && filterState.initMsgResponseFuture != null) {
			filterState.initMsgResponseFuture.set(cause);
		}
		endHandshake="N";
		nextFilter.exceptionCaught(session, cause);
	}
	
	@Override
	public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		if (context.isInitMsgSender() && context.isPermanent() && context.isInitMsgUse()){
			sendInitMsgRequest(session);
		}
		endHandshake="N";
		nextFilter.sessionOpened(session);
	}
	
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
//		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		if (endHandshake.equals("N") ){
			endHandshake = "Y";
		} else {
			nextFilter.messageReceived(session, message);
		}
	}
	
	private void sendInitMsgRequest(IoSession session) {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		final byte[] initMsgRequest = getInitMsgRequestFormatted();
		if (context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG) {
			adapter.addRecentSend(session, "InitMsgFilter | session idle -> <SEND> initMsg request - " 
					+ CommonLib.hexDump(initMsgRequest, true, context));
			logger.debug(context.getAdapterName() + " | InitMsgFilter | session idle -> <SEND> initMsg request - " 
					+ CommonLib.hexDump(initMsgRequest, true, context));
		}	
		IoSession sess = adapter.getSessionPool().acquire(session);
		try {
			if (filterState.initMsgResponseFuture == null) {				
				filterState.initMsgResponseFuture = new SimpleFuture();
			}
			final IoSession s = session;
//			final FilterState hState = filterState;
			if (Thread.currentThread().getName().startsWith("pool-")) {
				Thread.currentThread().setName(context.getAdapterName() + "-InitMsgFilter-" + Thread.currentThread().getName());
			}
			internalSendMessage(s, initMsgRequest);
		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | InitMsgFilter | error when send INIT_MSG request - " + e.getMessage(), e);
			logger.warn(context.getAdapterName() + " | InitMsgFilter | error when send INIT_MSG request - " + e.getMessage());
		} finally {
			if (sess != null) {
				adapter.getSessionPool().release(sess);
			}
		}
	}

	protected void internalSendMessage(IoSession session, Object message) throws Exception {	
		ByteMessage m = null;
		
		if (message instanceof byte[]) {
			m = new ByteMessage((byte[]) message);
		} else if (message instanceof ByteMessage) {
			m = (ByteMessage) message;
		} else if (message instanceof String) {
			m = new ByteMessage((String) message);
		} else {
			throw new Exception("Unknown data format");
		}
//		WriteFuture future = session.write(message);
		session.write(message);
	}
	
	protected byte[] getInitMsgRequestFormatted() {
		String message = context.getInitMsgRequest();
		//Polling ·ÎÁ÷ ¼öÁ¤  2017.05 KSH
		String str[] = message.split("%d");
		String endMsg = "";
		for(int i=0; i<str.length; i++){
			if(i<1){
				endMsg = str[i];
			}else{
				String newMsg = "%d"+str[i];
				Matcher matcher = Pattern.compile("%d\\{([a-zA-Z \\-]+)\\}").matcher(newMsg);
				if (matcher.find()) {
					String datePattern = matcher.group(1);
					DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(datePattern);
					endMsg = endMsg + newMsg.replaceAll("%d\\{([a-zA-Z \\-]+)\\}", dateFormat.format(ZonedDateTime.now()));
				}
			}
		}
		message = endMsg;
		if ("Y".equals(context.getLlInclude())) {		
			byte[] ll = new byte[context.getLlFieldLength()];    	
			ByteMessage mm = (new ByteMessage())
			.append(message.substring(0, context.getLlFieldOffset()))
			.append(ll)
			.append(message.substring(context.getLlFieldOffset()));
			return mm.getBytes();
		} else {
			return message.getBytes();
		}
	}	
}

