package com.eactive.eai.adapter.socket2.protocol.xecure.envelope;

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.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.XCException;
import com.sf.xc3.XC_v3;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoFilterChain;
import org.apache.mina.common.IoSession;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class XecureEnvelopeFilter extends XecureFilter {
	public XecureEnvelopeFilter(ISocketAdapter adapter) {
		super(adapter);
	}
	
	@Override
	public synchronized void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
		if (isConfigLoaded) {
			return;
		}
		
		File file = new File(context.getXecureConfigPath());
		
    	File pf = file.getParentFile();
    	if(pf == null) {
    		throw new Exception("'cert file path not found - " + file.getAbsolutePath());	    		
    	}
    	
		String certPath = null;
		
		if(pf.getParentFile() != null) {
    		certPath = pf.getParentFile().getPath() + File.separator + "cert";
    	}
    	else {
    		throw new Exception("'cert file path not found - " +pf.getAbsolutePath());
    	}
		
		Properties prop = loadXecureConfigFile(context.getXecureConfigPath());

		/**
		 * local cert
		 */
	    String certFile = prop.getProperty("Local Certificate File");
	    if (certFile == null) {
	    	throw new Exception("'Local Certificate File' property does not exist in xecure config file - " + context.getXecureConfigPath());
	    }
	    try {
	    	cert = readFile(certPath + File.separator + certFile);
	    } catch (FileNotFoundException e) {
	    	throw new Exception(context.getAdapterName() + " | " + getClass().getSimpleName() + " | Local Certificate File '" + certFile + "' does not exist.");
	    }
		if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE ) {
			logger.debug(context.getAdapterName() + " | " + getClass().getSimpleName() + " | load local cert file - " + certPath + File.separator + certFile);
		}
	    
	    /**
	     * local key
	     */
	    String keyFile = prop.getProperty("Local Secret Key File");
	    if (keyFile == null) {
	    	throw new Exception("'Local Secret Key File' property does not exist in xecure config file - " + context.getXecureConfigPath());
	    }
	    try {
	    	key = readFile(certPath + File.separator + keyFile);
	    } catch (FileNotFoundException e) {
	    	throw new Exception(context.getAdapterName() + " | " + getClass().getSimpleName() + " | Local Secret Key File '" + keyFile + "' does not exist.");
	    }
		if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE) {
			logger.debug(context.getAdapterName() + " | " + getClass().getSimpleName() + " | load local key file - " + certPath + File.separator + keyFile);
		}
	    
	    /**
	     * password
	     */
	    String passwordString = prop.getProperty("Local Password");
	    if (passwordString == null) {
	    	throw new Exception("'Local Password' property does not exist in xecure config file - " + context.getXecureConfigPath());
	    }			    
//		if (context.getTraceLevel() >= context.TRACE_LEVEL_TRACE) {
//			logger.debug(context.getAdapterName() + " | read password - *****");
//		}
		password = passwordString;
		
    	/**
    	 * remote cert
    	 */
	    String remoteCertFile = context.getXecureRemoteCert();    
	    try {
	    	remoteCert = readFile(certPath + File.separator + remoteCertFile);
	    } catch (FileNotFoundException e) {
	    	throw new Exception(context.getAdapterName() + " | " + getClass().getSimpleName() + " | xecure.remote.cert '" + remoteCertFile + "' file does not exist.");
	    }
		if (context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE) {
			logger.debug(context.getAdapterName() + " | " + getClass().getSimpleName() + " | load remote cert (public key) file - " + certPath + File.separator + remoteCertFile);
		}
		
    	isConfigLoaded = true;
	}
	
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		ByteMessage m = null;
		
		if (message instanceof byte[]) {
			m = new ByteMessage((byte[]) message);
		} else if (message instanceof ByteMessage) {
			m = (ByteMessage) message;
		} else if (message instanceof String) {
			m = new ByteMessage((String) message);
		} else {
			throw new Exception("Unknown data format");
		}  

		try {
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
			XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
	
			switch (filterState.state) {		
			case STATE_MESSAGE:
				if ("Y".equals(context.getLlInclude())) {
					ByteMessage bm = new ByteMessage(m.getBytes(0, context.getLlFieldOffset()));
					bm.append(m.getBytes(context.getLlFieldOffset() + context.getLlFieldLength()));
					m = bm;
				} 				
				m = new ByteMessage(xc.envdec(m.getBytes(), cert, key, password));

				if ("Y".equals(context.getLlInclude())) {
					ByteBuffer buffer = ByteBuffer.allocate(context.getLlFieldLength() + m.size());
					
					int size = m.size();
					if ("Y".equals(context.getLlFieldInclude())) {
						size = size + context.getLlFieldLength();
					}
					if ("Y".equals(context.getLlOffsetExclude())) {
						size = size - context.getLlFieldOffset();
					}		
					
					byte[] ll = null;					
					if ("CHAR".equals(context.getLlFieldType())) {
						ll = CommonLib.getFormatString(size, context.getLlFieldLength()).getBytes();
					} else {
						ll = new byte[context.getLlFieldLength()];
						for (int i = context.getLlFieldLength()-1; i>=0; i--) {
							ll[i] = (byte) (size % 256);
							size = size / 256;
						}
					}
					
					buffer.put(m.getBytes(0, context.getLlFieldOffset()));
					buffer.put(ll);  							
			        buffer.put(m.getBytes(context.getLlFieldOffset()));
			        buffer.flip();
			        m.setByteBuffer(buffer);
				} 

				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | XecureEnvelopeFilter | messageReceived() - after envdec - " + m.hexDump());
				}
				break;
		
			default:
				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.messageReceived(session, m);
	}

	@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");
		}  

		try {
			FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
			XC_v3 xc = (XC_v3) session.getAttribute(XecureConstants.XECURE);
	
			switch (filterState.state) {			
			case STATE_MESSAGE:	
				if ("Y".equals(context.getLlInclude())) {
					ByteMessage bm = new ByteMessage(m.getBytes(0, context.getLlFieldOffset()));
					bm.append(m.getBytes(context.getLlFieldOffset() + context.getLlFieldLength()));
					bm = new ByteMessage(xc.envenc(bm.getBytes(), remoteCert));	
					m = new ByteMessage();
					
					int size = bm.size();
					if ("Y".equals(context.getLlFieldInclude())) {
						size = size + context.getLlFieldLength();
					}
					if ("Y".equals(context.getLlOffsetExclude())) {
						size = size - context.getLlFieldOffset();
					}					
					String lenStr = CommonLib.getFormatString(size, context.getLlFieldLength());
					
					m.append(bm.getBytes(0, context.getLlFieldOffset()))
					 .append(lenStr.getBytes())
					 .append(bm.getBytes(context.getLlFieldOffset()));		
				} else {
					m = new ByteMessage(xc.envenc(m.getBytes(), remoteCert));
				}


				//m = (new ByteMessage(m.getBytes(0, 7))).append(xc.envenc(m.getBytes(7), remoteCert));	
				
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_DEBUG ) {
					logger.debug(context.getAdapterName() + " | XecureEnvelopeFilter | filterWrite() - after envenc - " + m.hexDump());
				}
				break;
				
			default:
				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);
		}

		ByteBuffer buffer = ByteBuffer.wrap(m.getBytes());
		nextFilter.filterWrite(session, new WriteRequest(buffer, writeRequest.getFuture(), writeRequest.getDestination()));
    }
}
