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

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.common.SMSLogger;
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.initech.inisafenet.HandShakeManager;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoSession;


public class InisafeClientFilter extends InisafeFilter {
	public InisafeClientFilter(ISocketAdapter adapter) {
		super(adapter);
	}	

	
	@Override
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		HandShakeManager inisafeSession = new HandShakeManager(context.getInisafeConfigPath());
//		inisafeSession.TEST_MODE = false;
		session.setAttribute(INISAFE_SESSION_KEY, inisafeSession);
		
		process(nextFilter, session, null);
	}
	
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		ByteMessage m = (ByteMessage) message;		
		process(nextFilter, session, m);
	}	
	
	protected void process(NextFilter nextFilter, IoSession session, ByteMessage message) throws Exception {
		try {
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
			if (filterState == null) {
				filterState = new FilterState();
				session.setAttribute(FILTER_STATE_KEY, filterState);
			}
			HandShakeManager inisafeSession = (HandShakeManager) session.getAttribute(INISAFE_SESSION_KEY);
	
			/**
			 * 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. ");
				}
				session.setIdleTime(IdleStatus.BOTH_IDLE, context.getTimeout() - 5);
				/**
				 * change state to INIT
				 */
				filterState.state = State.STATE_INIT;
				session.setAttribute(SESSION_STATE, filterState.state.toString());

				/**
				 * create INIT key
				 */
				byte[] initKey = inisafeSession.initHandShakeClient(null);				
				String initIndividualLL = CommonLib.getFormatString(initKey.length+4, 4);
		        //byte[] paddedInitKey = StringUtils.rightPad(new String(initKey), 92).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() >= context.TRACE_LEVEL_DEBUG) {
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | <SEND> KEY_INIT");
				}
				session.write(initIndividualBytes);
				
				/**
				 * change state to UPDATE
				 */
				filterState.state = State.STATE_UPDATE;
				setSessionState(session, filterState.state.toString());
				break;
				
			case STATE_UPDATE:
				if (context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG) {
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | <RECV> KEY_INIT response");
				}
				
				/**
				 * send UPDATE
				 */	
				byte[] updateKey = inisafeSession.updateHandShakeClient(message.getBytes());
				String updateIndividualLL = CommonLib.getFormatString(updateKey.length+4, 4);
		        //byte[] paddedUpdateKey = StringUtils.rightPad(new String(updateKey), 268).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() >= context.TRACE_LEVEL_DEBUG) {
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | <SEND> KEY_UPDATE");
				}	
				session.write(updateIndividualBytes);
				
				/**
				 * change state to FINAL
				 */
				filterState.state = State.STATE_FINAL;
				setSessionState(session, filterState.state.toString());	

				break;
				
			case STATE_FINAL:
				if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | <RECV> KEY_UPDATE response");
				}

				/**
				 * send FINAL
				 */
				byte[] finalKey = inisafeSession.finalHandShakeClient(message.getBytes());
				String finalIndividualLL = CommonLib.getFormatString(finalKey.length+4, 4);
		        //byte[] paddedFinalKey = StringUtils.rightPad(new String(finalKey), 92).getBytes();
				
				buffer = ByteBuffer.allocate(4+finalKey.length).limit(4+finalKey.length);
		        buffer.put(finalIndividualLL.getBytes());
		        buffer.put(finalKey);
		        
		        byte[] finalIndividualBytes = new byte[4+finalKey.length];
		        buffer.position(0).get(finalIndividualBytes);
	
				if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | <SEND> KEY_FINAL");
				}	
				session.write(finalIndividualBytes);

				/**
				 * change state
				 */
				filterState.state = State.STATE_MESSAGE;
				setSessionState(session, filterState.state.toString());	

				/**
				 * finish handshake
				 */
				if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
					adapter.addRecentLog(context.getAdapterName() + " | InisafeClientFilter | handshake finished ...");
					logger.debug(context.getAdapterName() + " | InisafeClientFilter | handshake finished ...");
				}	
				
				nextFilter.sessionCreated(session);
				nextFilter.sessionOpened(session);

				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() + " | inisafe error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | inisafe error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | inisafe 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() + " | inisafe error - " + e.getMessage(), e);
		}
	}		
}
