package com.eactive.eai.rms.client;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.InitializingBean;

import com.eactive.eai.rms.client.common.AbstractSocketClient;
import com.eactive.eai.rms.client.context.RmsProperties;
import com.eactive.eai.rms.onl.vo.MonitorDataVo;

/**
 * The Class SocketClientUdpImpl.
 * <ul>
 * <li>1. Function :</li>
 * <li>2. Summary :</li>
 * <li>3. Notification :</li>
 * </ul>
 * 
 * @author  : $Author: 윤창용 $
 * @version : $Revision: 1.12 $
 */
public class SocketClientUdpImpl extends AbstractSocketClient implements
        InitializingBean {

    /** F/W logger. */
    private static com.eactive.eai.common.util.Logger logger = com.eactive.eai.common.util.Logger.getLogger(com.eactive.eai.common.util.Logger.LOGGER_CLIENT);


	private Set<DatagramChannel> dChannels = new HashSet<DatagramChannel>();

	
    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public synchronized void afterPropertiesSet() throws Exception {
        if( logger.isDebugEnabled() ) {
            logger.debug("RMS_DEBUG:start afterPropertiesSet()" );
        }
        connect();
        
        if( logger.isDebugEnabled() ) {
            logger.debug("RMS_DEBUG:end   afterPropertiesSet()" );
        }        
    }

    private void connect() throws Exception {
    	logger.info("SocketClientUdpImpl] connection start");
    	destroy();
    	
    	String ipString = rmsProperties.getStringProperty(RmsProperties.RMS_SERVER_IP_KEY);
    	int port = rmsProperties.getIntProperty(RmsProperties.RMS_SERVER_PORT_KEY);
        if (ipString != null) {
            String[] ips = ipString.split(",");

            if (ips != null && ips.length > 0) {
                for (int i = 0; i < ips.length; i++) {
                	DatagramChannel ch = null;
                	try{
						ch = DatagramChannel.open();
						InetSocketAddress ad = new InetSocketAddress(ips[i], port);
					    ch.connect(ad);
					    logger.info("SocketClientUdpImpl] upd address hostname="+ad.getHostName()+", port="+ad.getPort());
                	}catch(Exception e){
                		if (ch!=null) try{ch.disconnect();}catch(Exception e1){}
                		logger.error("SocketClientUdpImpl] udp connection error "+e.getMessage(),e);
                		continue;
                	}
                	this.dChannels.add(ch);
                }
            }
        }
    	logger.info("SocketClientUdpImpl] connection end");
    }

    /* (non-Javadoc)
     * @see com.eactive.eai.rms.client.SocketClient#send(com.eactive.eai.rms.vo.MonitorDataVo)
     */
    public void send(final MonitorDataVo vo) {
         
        // 현재 900k 이지만 테스트 수행수 적정 사이즈로 조정한다.
    	ByteBuffer buffer = ByteBuffer.allocate(1024*10);
    	buffer.position(4);
    	try {
			try {
	            ObjectOutputStream out = new ObjectOutputStream(asOutputStream(buffer)) {
	                protected void writeClassDescriptor(ObjectStreamClass desc)
	                        throws IOException {
	                    String className = desc.getName();
	                    if (primitiveTypeNames.contains(className)) {
	                        write(0);
	                        super.writeClassDescriptor(desc);
	                    } else {
	                        write(1);
	                        writeUTF(desc.getName());
	                    }
	                }
	            };
	            out.writeObject(vo);
	            out.flush();
	        } catch (IOException e) {
	            throw e;
	        }
	        
	        int size = buffer.position() - 4;
	        buffer.position(0);
	        buffer.putInt(size);
	        buffer.position(4+size);
	        
	        buffer.flip();
            for (DatagramChannel ch : dChannels) {
            	try{
            		if (ch.isConnected()){
            			buffer.position(0);
		        		int written = ch.write(buffer);
		        		logger.debug("written="+written);
            		}
            	}catch(Exception e){
            		logger.error("RMS_ERROR : channel write error remote address[" +ch.getRemoteAddress()+"]" ,e);
            	}catch(Throwable t){
            		logger.error("RMS_ERROR : channel write error remote address[" +ch.getRemoteAddress()+"]" ,t);
            	}
            }
            
        } catch (Exception e) {
            // 현재 AIX 의 udp_send buffer size 가 1M 로 설정되어 있음. ( 2009.02.23 )
        	logger.error("RMS_ERROR : UDP 버퍼 사이즈 에러  buffer.limit()=" + buffer.limit() ,e);
        }

    }
    
    public OutputStream asOutputStream(final ByteBuffer buffer) {
        return new OutputStream() {
            public void write(byte[] b, int off, int len) {
                buffer.put(b, off, len);
            }

            public void write(int b) {
                buffer.put((byte) b);
            }
        };
    }

    public void destroy() {
        for (DatagramChannel ch : dChannels) {
            if (ch != null) {
                try { 
                    ch.disconnect(); 
                } catch(Exception ex) {
                	logger.error("udp destory error="+ex.getMessage(),ex);
                }
            }
        }
        dChannels.removeAll(new HashSet<DatagramChannel>());
    }
    
    private static final Set primitiveTypeNames = new HashSet();
    
    static {
        primitiveTypeNames.add("void");
        primitiveTypeNames.add("boolean");
        primitiveTypeNames.add("byte");
        primitiveTypeNames.add("char");
        primitiveTypeNames.add("short");
        primitiveTypeNames.add("int");
        primitiveTypeNames.add("long");
        primitiveTypeNames.add("float");
        primitiveTypeNames.add("double");
    }
}