package com.eactive.eai.adapter.socket2.protocol.inisafepayload.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.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;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;


public class InisafePayloadClientFilter extends InisafePayloadFilter {
	public InisafePayloadClientFilter(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;
		}
		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 {
		
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		if (filterState == null) {
			filterState = new FilterState();
			session.setAttribute(FILTER_STATE_KEY, filterState);
		}
		
		try {

			HandShakeManager inisafeSession = (HandShakeManager) session.getAttribute(INISAFE_SESSION_KEY);
	
			/**
			 * State machine
			 */
			ByteBuffer buffer = null;
			
			switch (filterState.state) {
			case STATE_INIT:
				/**
				 * change state to INIT
				 */
				filterState.state = State.STATE_INIT;
				session.setAttribute(SESSION_STATE, filterState.state.toString());

				/**
				 * create INIT key
				 */
				byte[] initKey = inisafeSession.initHandShakeClient(null);		
				if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG) {
					logger.debug(Arrays.toString(initKey) + " | InisafePayloadClientFilter | initKey Value");
				}
				buffer = ByteBuffer.allocate(context.getSendPayloadLength()).limit(context.getSendPayloadLength());

		        buffer.put(initKey);
		        while(buffer.hasRemaining()){
		        	buffer.put(" ".getBytes());
		        }				        
		        buffer.position(0);

		        logger.debug(buffer + " | InisafePayloadClientFilter | Buffer Value");
		        
				if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG) {
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | <SEND> KEY_INIT");
				}
				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() + " | InisafePayloadClientFilter | <RECV> KEY_INIT response");
				}
				
				/**
				 * send UPDATE
				 * null Padding
				 */
				
				if(message == null) {
					throw new Exception("receive message is null");
				}
				
				byte[] updateKey = inisafeSession.updateHandShakeClient(message.getBytes(0,1496));
				if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG) {
					logger.debug(Arrays.toString(updateKey) + " | InisafePayloadClientFilter | Update Value");
				}
				
				/**
				 * 기관요청에 의해 UpdateHandshake 시 ID-PASSWORD 자리를 스페이스 32바이트 송신   
				 */
				String idpw32 = "                                ";
				buffer = ByteBuffer.allocate(context.getSendPayloadLength()).limit(context.getSendPayloadLength());
				buffer.put(updateKey);
		        while(buffer.hasRemaining()){
		        	buffer.put(" ".getBytes());
		        }		
				buffer.position(384);
				buffer.put(idpw32.getBytes());
		        buffer.position(0);
		            
				if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG) {
					logger.debug(buffer + " | InisafePayloadClientFilter | Buffer Value");
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | <SEND> KEY_UPDATE");
				}	
				session.write(buffer);
				
				/**
				 * 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() + " | InisafePayloadClientFilter | <RECV> KEY_UPDATE response");
				}

				/**
				 * send FINAL
				 * null Padding
				 */
				if(message == null) {
					throw new Exception("message is null");
				}
				byte[] finalKey = inisafeSession.finalHandShakeClient(message.getBytes(0,116));
				
				buffer = ByteBuffer.allocate(context.getSendPayloadLength()).limit(context.getSendPayloadLength());
				buffer.put(finalKey);
		        while(buffer.hasRemaining()){
		        	buffer.put(" ".getBytes());
		        }		
				/**
				 * 20100515 JGY
				 * 금융기관인증정보생성 : 은행코드(3) + ID(8) + Password(20)
				 */
				String tmp = context.getInisafeIdPassword();
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | tmp : " + tmp.toString());
				}
				ByteMessage id_pw = new ByteMessage(inisafeSession.i_encrypt(tmp.getBytes()));
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | id_pw : " + id_pw.toString());
				}

				buffer.position(96);
			    buffer.put(id_pw.getBytes());	
		        buffer.position(0);
	
				session.write(buffer);
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					adapter.addRecentLog(context.getAdapterName() + " | InisafePayloadClientFilter | handshake finished ...");
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | <SEND> KEY_FINAL" + CommonLib.hexDump(buffer, context));
				}	

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

				nextFilter.sessionCreated(session);
				nextFilter.sessionOpened(session);

				break;
				
			case STATE_MESSAGE:
				
				if(message == null) {
					throw new Exception("receive message is null");
				}
				byte[] 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() + " | InisafePayloadClientFilter | hashBuf" + CommonLib.hexDump(hashBuf, true, context));
					logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | verify" + CommonLib.hexDump(verify, true, context));
				}
				
				String verifyString = new String(verify);
				String hashBufString = new String(hashBuf);
				
				if(!hashBufString.equals(verifyString)) {
					
					if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
						adapter.addRecentLog(context.getAdapterName() + " | InisafePayloadClientFilter | Integrity Verification Fail");
						logger.debug(context.getAdapterName() + " | InisafePayloadClientFilter | Integrity Verification Fail");
					}
					nextFilter.filterClose(session);
					adapter.getSessionPool().remove(session);
					session.close();					
					throw new Exception(context.getAdapterName() + " | initech error - ");
					
				} else {
					
					/**
					 * 메시지 복호화  
					 */										
					byte[] dBuf = inisafeSession. i_decrypt(bm);                    
					
					/**
					 * 암호화 처리된 업무 전문의 맨 끝 16 Byte를 
					 * 다음 업무 전문의 암/복호 처리를 위한 보조 암호키로 사용할 수 있도록 이니셜벡터 처리 
					 */
					byte[] iv = new byte[16];
					System.arraycopy(message.getBytes(), bm.length - 16, iv, 0, 16);
					inisafeSession.setIV(iv);
					
					/**
					 * 폴링전문 처리 
					 * 온라인 : 0100P        00409299999910000000일자(14}000                  N                                  P
					 * 일  괄 : 0023KoFC-OLB_POLLINGTESTREQ  
					 */
                    byte[] eBuf = new byte[7]; 
                    System.arraycopy(dBuf, 20, eBuf, 0, 7);

                    String eBufString = new String(eBuf);
                    
                    if ("9999910".equals(eBufString) || "TESTREP".equals(eBufString)) {
                    	
        			    adapter.addRecentRecv(session, "PollingFilter | <RECV> polling request - " + CommonLib.hexDump(dBuf, true, context));
                    	break;
                    	
                    } else if ("9999900".equals(eBufString)) {
                    	
                        byte[] p2Buf = new byte[21];
                        byte[] p4Buf = new byte[79];
                        
                        System.arraycopy(dBuf,  4, p2Buf, 0, 21);
                        System.arraycopy(dBuf, 26, p4Buf, 0, 79);                    	
        				ByteMessage polling = new ByteMessage("0100".getBytes()).append(p2Buf).append("1".getBytes()).append(p4Buf);
        			    adapter.addRecentSend(session, "PollingFilter | <SEND> polling response - " + polling.hexDump());
        			    
        				session.write(polling);
                    	
                    	break;
                    
                    } else if ("TESTREQ".equals(eBufString)) {
                   	
                        ByteMessage polling = new ByteMessage("0023KoFC-OLB_POLLINGTESTREP".getBytes());       				
        			    adapter.addRecentSend(session, "PollingFilter | <SEND> polling response - " + polling.hexDump());
        			    
                        session.write(polling);
                   	
                    	break;
                   }

					/**
					 * 메시지 길이 파싱  
					 */
                    byte[] byteRecvLL = new byte[4]; 
                    System.arraycopy(dBuf, 0, byteRecvLL, 0, 4);

        			int intRecvLL = Integer.parseInt(new String(byteRecvLL));

        			/**
					 * 메시지 길이만큼만 업무 프로세스로 송신  
					 */
					buffer = ByteBuffer.allocate(intRecvLL+4).limit(intRecvLL+4);
					byte[] byteRecvBuf = new byte[intRecvLL+4]; 
                    System.arraycopy(dBuf, 0, byteRecvBuf, 0, intRecvLL+4);
    				buffer.put(byteRecvBuf);
    				buffer.position(0);
    					
    				message = new ByteMessage(buffer);
    				
				}
					
				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.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);
		}
	}		
}
