package com.eactive.eai.adapter.socket2.protocol.fixedheader.ack;

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.IoFilterChain;
import org.apache.mina.common.IoSession;


abstract public class AckFilter extends IoFilterAdapter {
	final protected Logger logger;
    final protected Logger errLogger;

	protected static final String FILTER_STATE_KEY = AckFilter.class.getName() + ".STATE";
	protected static final String SESSION_STATE = "SESSION_STATE";

	protected static enum State {
		STATE_HANDSHAKE_SERVER_WAIT_HELLO,
		STATE_HANDSHAKE_CLIENT_WAIT_HELLO_ACK, 
		STATE_WAIT_ACK,
		STATE_SEND_ACK,
		STATE_MESSAGE;
	}

	protected static class FilterState {
		public State state = null;
	}

    protected SocketAdapterContext context;
	protected ISocketAdapter adapter;

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

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

	@Override
	public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
		if (parent.contains(AckFilter.class)) {
			throw new IllegalStateException("A filter chain cannot contain more than one AckFilter.");
		}
	}
	
	@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);
		}
	}
	
	@Override
	public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
	}
	
	@Override
	public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);

		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");
		}

		if (filterState.state == State.STATE_MESSAGE && m.size() != 3 && !"ACK".equals(m.getString())) {
			filterState.state = State.STATE_WAIT_ACK;
			setSessionState(session, filterState.state.toString());
		}
	}
	
	protected void sendAckMessage(IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);

		if (context.getTraceLevel() >= context.TRACE_LEVEL_TRACE) {
			logger.debug(context.getAdapterName() + " | <SEND> ACK ");
		}		

		filterState.state = State.STATE_SEND_ACK;
		setSessionState(session, filterState.state.toString());
		
//		WriteFuture future = session.write("ACK");
		session.write("ACK");
		//future.join(context.getTimeout() * 1000L);
	}
	
	protected void setSessionState(IoSession session, String state) {
		session.setAttribute(SESSION_STATE, state);
	}
}
