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

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.common.SMSLogger;
import com.eactive.eai.adapter.socket2.config.SocketAdapterContext;
import com.eactive.eai.adapter.socket2.config.SocketAdapterManager;
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.initech.inisafenet.HandShakeManager;
import com.initech.inisafenet.exception.INISAFENetException;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;


public class InisafeNatnPayloadServerFilter extends InisafeNatnPayloadFilter {
	public InisafeNatnPayloadServerFilter(ISocketAdapter adapter) {
		super(adapter);
	}	
	
	
	@Override
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		try {
			//KeyStorageManager ksm = new KeyStorageManager(null);
			HandShakeManager inisafeSession = new HandShakeManager(context.getInisafeConfigPath());
			session.setAttribute(INISAFE_SESSION_KEY, inisafeSession);
		} catch(INISAFENetException e) {
			throw e;
		}
	}
	
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		ByteMessage m = (ByteMessage) message;		
		//validateMessage(m);		
		process(nextFilter, session, m);
	}	
	
	protected void process(NextFilter nextFilter, IoSession session, ByteMessage message) throws Exception {
		HandShakeManager inisafeSession = null;

		try {
			/**
			 * get filter state
			 */
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
			if (filterState == null) {
				filterState = new FilterState();
				session.setAttribute(FILTER_STATE_KEY, filterState);
			}
			
			/**
			 * check inisafe session
			 */
			inisafeSession = (HandShakeManager) session.getAttribute(INISAFE_SESSION_KEY);
			if (inisafeSession == null) {
				throw new Exception("inisafe session is not established");
			}
	
			/**
			 * state machine
			 */
			ByteBuffer buffer = null;
			
			switch (filterState.state) {
			case STATE_INIT:
				/*
				 * handshake timeout 설정함
				 * timeout 에서 5초 전 설정  
				 */
				if (context.getTimeout() <= 5 ) {
					throw new Exception("invalid timeout("+context.getTimeout()+") value . set timeout must be 5 or more. ");
				}
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | <RECV> KEY_INIT");
				}		

				/**
				 * send INIT response
				 */
				byte[] initKey = inisafeSession.initHandShake(message.getBytes());
				String initIndividualLL = CommonLib.getFormatString(initKey.length+4, 4);
		        //byte[] paddedInitKey = StringUtils.rightPad(new String(initKey), 1792).getBytes();
				
				buffer = ByteBuffer.allocate(4+initKey.length).limit(4+initKey.length);
		        buffer.put(initIndividualLL.getBytes());
		        buffer.put(initKey);
				
		        byte[] initIndividualBytes = new byte[4+initKey.length];
		        buffer.position(0).get(initIndividualBytes);

		        if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | <SEND> KEY_INIT response");
				}				
				session.write(initIndividualBytes);
				
				/**
				 * change state to UPDATE
				 */
				filterState.state = State.STATE_UPDATE;
				setSessionState(session, filterState.state.toString());				

				break;
				
			case STATE_UPDATE:
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | <RECV> KEY_UPDATE");
				}
				
				/**
				 * send UPDATE response
				 */
				byte[] updateKey = inisafeSession.updateHandShake(message.getBytes());
				String updateIndividualLL = CommonLib.getFormatString(updateKey.length+4, 4);
		        //byte[] paddedUpdateKey = StringUtils.rightPad(new String(updateKey), 108).getBytes();
				
				buffer = ByteBuffer.allocate(4+updateKey.length).limit(4+updateKey.length);
		        buffer.put(updateIndividualLL.getBytes());
		        buffer.put(updateKey);
				
		        byte[] updateIndividualBytes = new byte[4+updateKey.length];
		        buffer.position(0).get(updateIndividualBytes);

		        if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | <SEND> KEY_UPDATE response");
				}				
				session.write(updateIndividualBytes);

				/**
				 * change state to FINAL
				 */
				filterState.state = State.STATE_FINAL;
				setSessionState(session, filterState.state.toString());
				
				break;
				
			case STATE_FINAL:
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | <RECV> KEY_FINAL");
				}
				int result = inisafeSession.finalHandShake(message.getBytes());
				
				if (result !=1){
					throw new Exception("<RECV> invalid message during handshake .. : " + message);
				}
				
				
				/**
				 * change state to MESSAGE
				 */
				filterState.state = State.STATE_MESSAGE;
				setSessionState(session, filterState.state.toString());

				/**
				 * start client pair socket adapter (in same socket adapter group)
				 */
				ISocketAdapter[] adapters = SocketAdapterManager.getInstance().getSocketAdapters(adapter.getContext().getAdapterGroupName());
				for (int i=0; i<adapters.length; i++) {
					if (adapters[i].getContext().isClient()) {
						if (adapters[i].getState() == ISocketAdapter.State.STOPPED) {
							adapters[i].start().join();
						}
						break;
					}
				}	

				/**
				 * handshake finished
				 */
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					adapter.addRecentLog("InisafeNatnPayloadServerFilter | handshake finished ...");
					logger.debug(context.getAdapterName() + " | InisafeNatnPayloadServerFilter | handshake finished ...");
				}
				
				nextFilter.sessionCreated(session);
				nextFilter.sessionOpened(session);				
				//nextFilter.messageReceived(session, new ByteMessage("STARTED"));
				
				break;
				
			case STATE_MESSAGE:
				//validateMessage(message);		
				message = decrypt(message, session);
				nextFilter.messageReceived(session, message);
				break;
		
			default:
				throw new Exception("<RECV> invalid message during handshake .. : " + message);
			}
		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | initech error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | initech error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | initech error - " + e.getMessage(), e);
//			SMSLogger.loggingSMSWithCode(context.getAdapterName(), "RECEAINNA012");
			SMSLogger.loggingSMS4Soc(context.getAdapterName(), context.getDescription(), "RECEAINNA012");

			nextFilter.filterClose(session);

			adapter.getSessionPool().remove(session);
			session.close();
			
			throw new Exception(context.getAdapterName() + " | initech error - " + e.getMessage(), e);
		}
	}
}
