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

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;

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.protocol.ByteMessage;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.initech.inisafenet.HandShakeManager;
import com.initech.inisafenet.exception.INISAFENetException;
import com.initech.inisafenet.util.INISAFENetCryptoUtil;
import java.util.Arrays;


public class InisafePayloadServerFilter extends InisafePayloadFilter {
	public InisafePayloadServerFilter(ISocketAdapter adapter) {
		super(adapter);
	}	
	
	
	@Override
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		try {
			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;		
		
		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:
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | <RECV> KEY_INIT");
				}		

				/**
				 * send INIT response
				 */
				byte[] initKey = inisafeSession.initHandShake(message.getBytes());
				
				buffer = ByteBuffer.allocate(context.getRecvPayloadLength()).limit(context.getRecvPayloadLength());

				buffer.put(initKey);
		        buffer.position(0);

		        if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | <SEND> KEY_INIT response");
				}				
		        
				session.write(buffer);
				
				/**
				 * 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() + " | InisafePayloadServerFilter | <RECV> KEY_UPDATE");
				}
				
				/**
				 * send UPDATE response
				 * LL 데이터 전송 테스트 JGY
				 */
			//  LL 생성  				
			//	String updateLL = CommonLib.getFormatString(updateKey.length, 4);
			//  LL 파싱 	
			//	int intUpdateLL = Integer.parseInt(new String(message.getBytes(4000,4)));
			//	byte[] updateKey = inisafeSession.updateHandShake(message.getBytes(0, intUpdateLL));
				
				byte[] idpw32 = message.getBytes(384, 32);
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | id_pw : " + Arrays.toString(idpw32));
				}
				
				byte[] updateKey = inisafeSession.updateHandShake(message.getBytes());
			
				/**
				 * ByteBuffer 타입의 buffer가 버퍼 초기화가 될 수 없어 객체 신규생성
				 */				
				byte[] updateKeyBuffer = new byte[context.getRecvPayloadLength()];
				
				System.arraycopy(updateKey, 0, updateKeyBuffer, 0, updateKey.length);
		        if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | <SEND> KEY_UPDATE response");
				}				
				session.write(updateKeyBuffer);

				/**
				 * change state to FINAL
				 */
				filterState.state = State.STATE_FINAL;
				setSessionState(session, filterState.state.toString());
				
				break;
				
			case STATE_FINAL:
				
				inisafeSession.finalHandShake(message.getBytes(0,96));
				
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | <RECV> KEY_FINAL");
				}

				/**
				 * Client 에서 ID/Password를 송신하면 서버에서 복호화 후 검증 테스트 
				 */
				byte[] id_pw = message.getBytes(384, 32);
				id_pw = inisafeSession.i_decrypt(id_pw);
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | id_pw : " + Arrays.toString(id_pw));
				}
				
				/**
				 * change state to MESSAGE
				 */
				filterState.state = State.STATE_MESSAGE;
				setSessionState(session, filterState.state.toString());

				/**
				 * handshake finished
				 */
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					adapter.addRecentLog("InisafePayloadServerFilter | handshake finished ...");
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | handshake finished ...");
				}
				
				nextFilter.sessionCreated(session);
				nextFilter.sessionOpened(session);				
				
				break;
				
			case STATE_MESSAGE:	
				
				byte[] bm = new byte[4048];
				bm = message.getBytes(0, message.size()-48);
				byte[] verify  = message.getBytes(message.size()-48, 20);
				
				/**
				 * 메시지 HASH  
				 */
				INISAFENetCryptoUtil cryptoUtil = new INISAFENetCryptoUtil();
				byte[] hashBuf = cryptoUtil.getDigest(bm, "SHA1");
				
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | bm : " + CommonLib.hexDump(bm, true, context));
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | hashBuf : " + CommonLib.hexDump(hashBuf, true, context));
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | verify : " + CommonLib.hexDump(verify, true, context));
				}
				
//				if(!hashBuf.equals(verify)) {
				if(!Arrays.equals(hashBuf, verify)) {
							if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
						adapter.addRecentLog(context.getAdapterName() + " | InisafePayloadServerFilter | Integrity Verification Fail");
						logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | Integrity Verification Fail");
					}
					nextFilter.filterClose(session);
					adapter.getSessionPool().remove(session);
					session.close();				
					throw new Exception(context.getAdapterName() + " | initech error - ");
					
				} else {
					
					buffer = ByteBuffer.allocate(context.getSendPayloadLength()).limit(context.getSendPayloadLength());
					
					/**
					 * 암호화 처리된 업무 전문의 맨 끝 16 Byte를 
					 * 다음 업무 전문의 암/복호 처리를 위한 보조 암호키로 사용할 수 있도록 이니셜벡터 처리 
					 */
					byte[] iv = new byte[16];
					System.arraycopy(bm, 4048 - 16, iv, 0, 16);
					inisafeSession.setIV(iv);
					
					/**
					 * 메시지 복호화  
					 */
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | Before decrypt" + CommonLib.hexDump(bm, true, context));
					buffer.put(inisafeSession.i_decrypt(bm));
					buffer.position(0);
					
					message = new ByteMessage(buffer);
					logger.debug(context.getAdapterName() + " | InisafePayloadServerFilter | After decrypt" + message.hexDump());
					
				}
				
				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.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);
		}
	}
}
