package com.eactive.eai.adapter.socket2.protocol.xecure.welcome.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.eactive.eai.adapter.socket2.protocol.ProtocolHandler;
import com.eactive.eai.adapter.socket2.protocol.xecure.XecureConstants;
import com.eactive.eai.adapter.socket2.protocol.xecure.XecureErrorMessage;
import com.eactive.eai.adapter.socket2.protocol.xecure.XecureFilter;
import com.sf.xc3.Base64;
import com.sf.xc3.XCException;
import com.sf.xc3.XC_v3;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.WriteFuture;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

abstract public class XecureWelcomeFilter extends XecureFilter {
	public XecureWelcomeFilter(ISocketAdapter adapter) {
		super(adapter);
	}	
	
	@Override
    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
		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_DEBUG ) {
			logger.debug(context.getAdapterName() + " | XecureFilter | <SEND> " + m.hexDump());
		    adapter.addRecentSend(m.hexDump());
		}
		*/	    

		try {
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
//			XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
	
			switch (filterState.state) {			
			case STATE_MESSAGE:
//				String sessionState = (String) session.getAttribute(ProtocolHandler.SESSION_STATE);
				m = encrypt(m, session);
				if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | XecureWelcomeFilter | filterWrite() - after encryption - " + m.hexDump());
				}
				break;
				
			default:
				/*
				if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | XecureFixedClientFilter - <SEND> " + filterState.state + " - " + m.hexDump());
				}
				*/
				break;
			}
		} catch(XCException e) {
			String xcMessage = "";
			Matcher matcher = Pattern.compile("(\\-[0-9]+)").matcher(e.getMessage());
			if (matcher.find()) {
			    String key = matcher.group(1);
			    xcMessage = XecureErrorMessage.getErrorMessage(key);
			}
			adapter.addRecentError(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
			logger.error(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
			errLogger.error(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
//			SMSLogger.loggingSMSWithCode(context.getAdapterName(), "RECEAINNA012");
			SMSLogger.loggingSMS4Soc(context.getAdapterName(), context.getDescription(), "RECEAINNA012");
			throw new XCException(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage);
		}

		nextFilter.filterWrite(session, new WriteRequest(m, writeRequest.getFuture(), writeRequest.getDestination()));
    }

	protected byte[] format(byte[] msg, IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
//		XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);

		if (filterState == null) {
			filterState = new FilterState();
			session.setAttribute(FILTER_STATE_KEY, filterState);
		}
		
		ByteMessage m = new ByteMessage(msg);
		
		String sessionState = (String) session.getAttribute(ProtocolHandler.SESSION_STATE);
		byte[] bytes = null;		
        if (sessionState.startsWith("STATE_WELCOME")) {
			bytes = (new ByteMessage(String.format("%-7s", "0000000")))							
					.append("0")       // 암호화 타입 (0:plain 1:DES 2:Xecure Connect)
					.append(String.format("%16s", " "))
			        .append(m.getBytes())
	                .append("\r\n".getBytes())
			        .getBytes();
		} else if (sessionState.startsWith("STATE_KEY_")) {
			bytes = (new ByteMessage(String.format("%-7s", "0000000")))
					.append("0")       // 암호화 타입 (0:plain 1:DES 2:Xecure Connect)
					.append(String.format("%22s", "KB_DEV          |4001|"))
			        .append(m.getBytes())
			        .append("|\r\n".getBytes())
			        .getBytes();
		} else {
			bytes = (new ByteMessage(String.format("%-24s", "0000000")))
					.append("2")       // 암호화 타입 (0:plain 1:DES 2:Xecure Connect)
					.append(String.format("%16s", " "))
			        //.append(Base64.encode(xc.encode(m.getBytes())))  
					.append(m.getBytes())
		            .append("\r\n".getBytes())
			        .getBytes();
		}
		
		return bytes;
	}

	protected ByteMessage encrypt(ByteMessage m, IoSession session) throws Exception {
		XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
		byte[] bytes = (new ByteMessage(m.getBytes(0, 7+17)))
				        .append(Base64.encode(xc.encode(m.getBytes(7+17, m.size()-7-17))))
				        //.append("\r\n")
				        .getBytes();
		return new ByteMessage(bytes);		
	}

	/**
	 * BASIC/FIXED/WELCOME/B2BHEADER 종류에 따라 codec을 만들지 않고 이쪽에서 작업한다. ^^;
	 */
	protected ByteMessage decrypt(ByteMessage m, IoSession session) throws Exception {
		XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
		
		byte[] input = null;		

		if (m.getByte(7) != '2') {
			return m;
		}
		input = m.getBytes(7+17);

		byte[] output = null;		
		
		try {
			output = Base64.decode(input);
			output = xc.decode(output, true).getBytes();
			output = Base64.decode(output);
		} catch(Exception e) {
//			e.printStackTrace();
			String xcMessage = "";
			Matcher matcher = Pattern.compile("(\\-[0-9]+)").matcher(e.getMessage());
			
			if (matcher.find()) {
			    String key = matcher.group(1);
			    xcMessage = XecureErrorMessage.getErrorMessage(key);
			}
			
			throw new Exception("xecure - decode error. " + e.getMessage() + " | " + xcMessage);
		}
	
		ByteMessage result = new ByteMessage(output);
		ByteBuffer buffer = null;
		buffer = ByteBuffer.allocate(7 + 17 + output.length);
		String lenStr = CommonLib.getFormatString(7 + 17 + output.length, 7);	
		buffer.put(lenStr.getBytes());  							
        buffer.put(m.getBytes(7, 17));
        buffer.put(output);
        //buffer.put(m.getBytes(m.size()-2, 2));
        buffer.flip();
        result.setByteBuffer(buffer);

		if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
			logger.debug(context.getAdapterName() + " | <RECV> including LL | " + CommonLib.hexDump(buffer, context));
		}
		
		return result;
	}

	
	protected void internalSendMessage(IoSession session, Object message) throws Exception {
		try {
			//SocketAdapterManager.getInstance().memoryLog("SEND", session, context, message.toString());
	
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
			XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
	
			if (filterState == null) {
				filterState = new FilterState();
				session.setAttribute(FILTER_STATE_KEY, filterState);
			}
			
			ByteMessage m = null;
			if (message == null) {
				throw new Exception("cannot write null message.");
			} else if (message instanceof ByteMessage) {
				m = (ByteMessage) message;
			} else if (message instanceof String) {
				m = new ByteMessage((String) message);
			} else if (message instanceof byte[]) {
				m = new ByteMessage((byte[]) message);
			} else {
				throw new Exception("cannot write unknow format message.");
			}
			
			String sessionState = (String) session.getAttribute(ProtocolHandler.SESSION_STATE);
			byte[] bytes = null;		
            if (sessionState.startsWith("STATE_WELCOME")) {
				bytes = (new ByteMessage(m.getBytes(0, 7+17)))
				        .append(Base64.encode(xc.encode(m.getBytes(7+17, m.size()-7-17-2))))
		                .append(m.getBytes(m.size()-2))
				        .getBytes();
			} else if (sessionState.startsWith("STATE_KEY_")) {
				bytes = (new ByteMessage(m.getBytes(0, 7+23)))
				        .append(Base64.encode(xc.encode(m.getBytes(7+23, m.size()-7-23-3))))
				        .append(m.getBytes(m.size()-3))
				        .getBytes();
			} else {
				bytes = (new ByteMessage(m.getBytes(0, 7+17)))
		                .append(Base64.encode(xc.encode(m.getBytes(7+17, m.size()-7-17))))
		                //.append("\r\n")
		                .getBytes();
			}			
	
			if ( context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG ) {
				if (message instanceof byte[]) {
					logger.debug(context.getAdapterName() + " | <SEND> including LL | " + CommonLib.hexDump((byte[]) message, true, context));
				    adapter.addRecentSend(session, CommonLib.hexDump((byte[]) message, true, context));
				} else if (message instanceof String) {
					logger.debug(context.getAdapterName() + " | <SEND> including LL | " + CommonLib.hexDump(((String) message).getBytes(), true, context));
				    adapter.addRecentSend(session, message.toString());
				} else {
					logger.debug(context.getAdapterName() + " | <SEND> including LL | " + message);
				    adapter.addRecentSend(session, message.toString());
				}
			}

			WriteFuture future = session.write(bytes);
			future.join(context.getTimeout() * 1000L);
		} catch(XCException e) {
			String xcMessage = "";
			Matcher matcher = Pattern.compile("(\\-[0-9]+)").matcher(e.getMessage());
			
			if (matcher.find()) {
			    String key = matcher.group(1);
			    xcMessage = XecureErrorMessage.getErrorMessage(key);
			}
			
			adapter.addRecentError(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
			logger.error(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
			errLogger.error(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage, e);
//			SMSLogger.loggingSMSWithCode(context.getAdapterName(), "RECEAINNA012");
			SMSLogger.loggingSMS4Soc(context.getAdapterName(), context.getDescription(), "RECEAINNA012");
			throw new XCException(context.getAdapterName() + " | xecure error - " + e.getMessage() + " | " + xcMessage);
		}
	}	
}
