package com.eactive.eai.adapter.socket2.protocol.envelop.handshake;

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.sf.xc.XCException;
import com.sf.xc.XCUtil;
import org.apache.mina.common.IoFilterAdapter;
import org.apache.mina.common.IoFilterChain;
import org.apache.mina.common.IoSession;


public class EnvelopClientFilter extends IoFilterAdapter {
	
	protected final Logger logger;
	protected final Logger errLogger;

	private   static final String NEXT_FILTER      = EnvelopClientFilter.class.getName() + ".NextFilter";
	protected static final String FILTER_STATE_KEY = EnvelopClientFilter.class.getName() + ".STATE";
	protected static final String SESSION_STATE    = "SESSION_STATE";

	protected enum State {
		STATE_HANDSHAKE,
		STATE_MESSAGE;
	}

	protected static class FilterState {
		public State  state = State.STATE_MESSAGE;  //State.STATE_HANDSHAKE;
		public byte[] userKey = new byte[16];
		public int[]  roundKey = new int[32];
	}
	
    protected SocketAdapterContext context;
	protected ISocketAdapter adapter;
	
	public EnvelopClientFilter(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(EnvelopClientFilter.class)) {
			throw new IllegalStateException("A filter chain cannot contain more than one DesFilter.");
		}
		
		IoSession session = parent.getSession();
		session.setAttribute(NEXT_FILTER, nextFilter);
	}
	
	@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 sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		try {
			/**
			 * EnvelopClient init
			 */
			try {
			    byte[] cert = XCUtil.readFile("modules/envelop/cert/signCert.der");
//			    XCE2EClient client = new XCE2EClient(cert);
			    
//			    session.setAttribute("ENVELOP.CLIENT", client);
			} catch(Exception e) {
				logger.warn("ENVELOP.CLIENT set error", e);
			}
					
			nextFilter.sessionOpened(session);
			
		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | Envelop init error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | Envelop init error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | Envelop init error - " + e.getMessage(), e);
			
			try {
				adapter.getSessionPool().remove(session);
				session.close();
			} catch(Exception ignor) {
				logger.warn("session.close error", e);
			}
			
			throw new Exception(context.getAdapterName() + " | Envelop init error - " + e.getMessage());
		}
	}
	
	protected void setSessionState(IoSession session, String state) {
		session.setAttribute(SESSION_STATE, state);
	}

	
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		ByteMessage m = (ByteMessage) message;

		switch (filterState.state) {		
		case STATE_MESSAGE:
			/**
			 * decrypt
			 */
//	        byte[] decrypted = EnvelopCodec.e2eClientDecrypt(m.getBytes(), session);       
//	        if (decrypted == null || decrypted.length == 0) {
//	        	throw new Exception("received message is invalid - check if message is encrypted : " + m.hexDump());
//	        }
//	        m.setBytes(decrypted);

	        /**
			 * next filter
			 */
			nextFilter.messageReceived(session, m);
			break;
			
		default:
			throw new Exception("<RECV> invalid message during handshake .. : " + m.hexDump());
		}
	}
	
	@SuppressWarnings("static-access")
	@Override
    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		ByteMessage m = null;

		
		if (writeRequest.getMessage() instanceof byte[]) {
			m = new ByteMessage((byte[]) writeRequest.getMessage());
		} else if (writeRequest.getMessage() instanceof ByteMessage) {
			m = (ByteMessage) writeRequest.getMessage();
		} else if (writeRequest.getMessage() instanceof String) {
			m = new ByteMessage((String) writeRequest.getMessage());
		} else {
			throw new Exception("Unknown data format");
		}

		if ( context.getTraceLevel() >= context.TRACE_LEVEL_TRACE ) {
			logger.debug(context.getAdapterName() + " | EnvelopFilter | <SEND> " + filterState.state.toString() + " | " + m.hexDump());
		}
		
		try {

//			m = EnvelopCodec.e2eClientEncrypt(m.getBytes(), session);

		} catch(Exception xce) {
			adapter.addRecentError(context.getAdapterName() + " | envelop error - " + xce.getMessage(), xce);
			logger.error(context.getAdapterName() + " | envelop error - " + xce.getMessage(), xce);
			errLogger.error(context.getAdapterName() + " | envelop error - " + xce.getMessage(), xce);
			throw new XCException(context.getAdapterName() + " | envelop error - " + xce.getMessage());
		}

		/**
		 * next filter
		 */
		nextFilter.filterWrite(session, new WriteRequest(m, writeRequest.getFuture(), writeRequest.getDestination()));
    }	
	
}
