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

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.eactive.eai.adapter.socket2.protocol.xc3epb.Xc3EpbConstants;
import com.sf.xc3.XCException;
import com.sf.xc3.XC_v3;
import org.apache.mina.common.IoSession;


public class Xc3EpbClientFilter extends Xc3EpbFilter {
	public Xc3EpbClientFilter(ISocketAdapter adapter) {
		super(adapter);
	}	
	@Override
	public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);

		if (filterState == null) {
			filterState = new FilterState();
			filterState.state = State.STATE_KEYINIT;
			session.setAttribute(FILTER_STATE_KEY, filterState);
		}

		nextFilter.sessionCreated(session);
	}	
	@Override
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		XC_v3 xc = new XC_v3();
		xc.init(context.getXc3epbPassword(), context.getXc3epbConfigPath(), 1);//MODE 1 (client) 
		session.setAttribute(XC3_SESSION_KEY, xc);
		process(nextFilter, session, null);
	}
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		ByteMessage m = (ByteMessage) message;		
		process(nextFilter, session, m);
	}		
	public void process(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		ByteMessage m = (ByteMessage) message;
		
		
		try {
			XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);
			
			switch (filterState.state) {
			case STATE_KEYINIT:
					byte[] keyInit = xc.keyinitBin();
					
					ByteMessage mKeyInit = new ByteMessage()
							                .append("S".getBytes())
											.append("Y".getBytes())
											.append(keyInit);
					if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
						logger.debug(context.getAdapterName() + " | Xc3EpbClientFilter | <SEND> key init " + filterState.state.toString() + " | " + mKeyInit.hexDump());
						adapter.addRecentSendTrace(session, context.getAdapterName() + " | Xc3EpbClientFilter | <SEND> key init " + filterState.state.toString() + " | " + mKeyInit.hexDump());
					}					
					
					session.write(mKeyInit);
				
					filterState.state = State.STATE_KEYFINAL;
					session.setAttribute(FILTER_STATE_KEY, filterState);
					break;
			case STATE_KEYFINAL:
					if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
						logger.debug(context.getAdapterName() + " | Xc3EpbClientFilter | <RECV> " + filterState.state.toString() + " | " + m.hexDump());
						adapter.addRecentRecvTrace(session, context.getAdapterName() + " | Xc3EpbClientFilter | <RECV> " + filterState.state.toString() + " | " + m.hexDump());
					}					
					/**
					 * 메시지구분 [0|1] : S(세션키교환) / D (표준전문)  / P (폴링)
					 * 암화화여부 [1|1] : Y(세션키로 암호화된 메시지) / N (평문메시지)
					 */
					String messageType = new String(m.getBytes(Xc3EpbConstants.MESSAGE_TYPE_OFFSET, 1));
					String encryptedYn    = new String(m.getBytes(Xc3EpbConstants.ENCRYPTED_YN_OFFSET, 1));
					
					if("S".equals(messageType)){
						filterState.state = State.STATE_HANDSHAKE;
					}else if ("D".equals(messageType)){
						filterState.state = State.STATE_MESSAGE;
					}else{
						throw new Exception("not support message type ("+messageType+")");
					}
					session.setAttribute(FILTER_STATE_KEY, filterState);
					
					boolean isEncrypted = false;
					if ("Y".equals(encryptedYn)){
						isEncrypted = true;
					}else if ("N".equals(encryptedYn)){
						isEncrypted = false;
					}else {
						throw new Exception("not support encrypted code ("+encryptedYn+")");
					}
					session.setAttribute(ENCRYPTED_YN_KEY,isEncrypted);
					
					byte[] keydata = m.getBytes(2);
					int ret = xc.keyFinal(keydata);
					if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
						logger.debug(context.getAdapterName() + " | Xc3EpbClientFilter | <RECV> ret " + ret +" "+ filterState.state.toString() + " | " + new ByteMessage(keydata).hexDump());
						adapter.addRecentSend(session, context.getAdapterName() + " | Xc3EpbClientFilter | <RECV> ret " + ret +" "+ filterState.state.toString() + " | " + new ByteMessage(keydata).hexDump());
					}					
					if (ret == 300){
						byte[] retryKeyInit = xc.keyinitBin();
						ByteMessage mRetryKeyInit = new ByteMessage()
								 				.append("S".getBytes())
												.append("Y".getBytes())
												.append(retryKeyInit);
						
						if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
							logger.debug(context.getAdapterName() + " | Xc3EpbClientFilter | <SEND> retry key init " + filterState.state.toString() + " | " + mRetryKeyInit.hexDump());
							adapter.addRecentSend(session, context.getAdapterName() + " | Xc3EpbClientFilter | <SEND> retry key init " + filterState.state.toString() + " | " + mRetryKeyInit.hexDump());
						}
						session.write(mRetryKeyInit);
						filterState.state = State.STATE_KEYINIT;
						session.setAttribute(FILTER_STATE_KEY, filterState);
					}else{
						nextFilter.sessionCreated(session);
						nextFilter.sessionOpened(session);
						filterState.state = State.STATE_MESSAGE;
						session.setAttribute(FILTER_STATE_KEY, filterState);
					}
			
				
				break;
				
			case STATE_MESSAGE:
				m = decrypt(m, session);
				nextFilter.messageReceived(session, m);
				break;
				
			default:
				throw new Exception("<RECV> invalid message during handshake | " + m.hexDump());
			}
			
		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | xc3epb error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | xc3epb error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | xc3epb 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() + " | xc3epb error - " + e.getMessage());
		}
	}
	@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() + " | XC3EpbFilter | <SEND> " + filterState.state.toString() + " | " + m.hexDump());
		}
		
		try {
			switch (filterState.state) {
				case STATE_KEYINIT :
				case STATE_KEYFINAL :
				case STATE_HANDSHAKE :

					break;
				case STATE_MESSAGE:
					m = encrypt(m, session);
					break;
				default :
					break;
					
			}
			
			
			
		} catch(XCException xce) {
			adapter.addRecentError(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			logger.error(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			errLogger.error(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			throw new XCException(context.getAdapterName() + " | xc3epb error - " + xce.getMessage());
		}

		/**
		 * next filter
		 */
		nextFilter.filterWrite(session, new WriteRequest(m, writeRequest.getFuture(), writeRequest.getDestination()));
    }
	protected ByteMessage encrypt(ByteMessage m, IoSession session) throws Exception {
		
		ByteMessage em = new ByteMessage();
//		if (context.isPollingUse() && isPollingPattern(m)) {
//			//메시지구분/암호화여부추가
//			em.append("P".getBytes()).append("Y".getBytes());
//		}else{
//			//메시지구분/암호화여부추가
//			em.append("D".getBytes()).append("Y".getBytes());
//		}

		XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);
		byte[] encrypted = xc.encode(m.getBytes(2));
		em.append(m.getBytes(0,2));
		em.append(encrypted);
		return em;
	}
	protected ByteMessage decrypt(ByteMessage m, IoSession session) throws Exception {
		/**
		 * 암호화 여부 체크
		 */
		if ('Y' != m.getByte(Xc3EpbConstants.ENCRYPTED_YN_OFFSET)){
			ByteMessage dm = new ByteMessage(m.getBytes(2));
			return dm;
		}

		XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);
		
		byte[] decrypted = xc.decode(m.getBytes(2));
		
		ByteMessage dm = new ByteMessage(m.getBytes(0,2));
		dm.append(decrypted);
		
		if(context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE){
			logger.debug(context.getAdapterName() + " | Xc3EpbFilter | decrypt " + dm.hexDump());
		}
		return dm;
	}
}
